poorna2152 / nballerina

WebAssembly Backend for the nBallerina compiler
https://ballerina.io/
Apache License 2.0
4 stars 0 forks source link

Performance check #34

Closed poorna2152 closed 2 years ago

poorna2152 commented 2 years ago

This was what I was using to represent subtype supertype relationship.

  (type $BoxedInt (struct (field $type i32) (field $val i64)) (extends $Any))
  (type $Any (struct (field $type i32))) 

But apparently use of extend and using rtt.sub is not needed. Since both $BoxedInt and $Any struct definition start with a i32 field $Any is implicitly going to be a supertype of $BoxedInt. https://github.com/WebAssembly/gc/blob/main/proposals/gc/Overview.md#subtyping

Thus when we pass a $BoxedInt to below function it works.

(func $get_type_code (param $0 (ref $BoxedInt))  (result i32)
    (struct.get $Any $type
      (local.get $0) 
      (rtt.canon $Any)))

But, when we change the parameter of the above function to eqref and cast the struct to $Any within the function, the code fails.

(func $get_type_code (param $0 eqref) (result i32)
    (struct.get $Any $type
      (ref.cast
        (ref.as_data
          (local.get $0)) 
        (rtt.canon $Any))))

If this failure is an intentional one then I think we cant rely on this subtype supertype representation. Then we might have to go back to my initial implementation where I used the type of the struct to get the type of a variable.

manuranga commented 2 years ago

1.

the code fails.

What is the error?

2. See: https://github.com/WebAssembly/gc/blob/main/proposals/gc/MVP.md#runtime-types Can we use (ref.cast (ref.as_data (local.get $0)) (global.get $rttAny)) like shown in above?

poorna2152 commented 2 years ago
  1. RuntimeError: illegal cast
  2. Checked it. It didnt work. When we define globals as such we still have to use the base global vector( rttAny) in casting so I think thats why it wont work.
    
    (global $rttA (rtt $Any) (rtt.canon $Any))  
    (global $rttB (rtt $BoxedInt) (rtt.canon $BoxedInt))

(func $get_type_code (param $0 eqref) (result i32) (struct.get $Any $type (ref.cast (ref.as_data (local.get $0)) (global.get $rttA))))

poorna2152 commented 2 years ago
(module 
  (type $BoxedInt (struct (field $type i32) (field $val i64)))
  (type $Any (struct (field $type i32))) 
  (import "console" "log" (func $println (param i32)))  
  (export "main" (func $main)) 
  (export "get_type" (func $get_type)) 
  (func $main
    (local $0 eqref)
    (local.set $0
      (call $int_to_tagged
        (i64.const 21)))
    (call $println
        (call $get_type
            (local.get $0))))
  (func $int_to_tagged (param $0 i64) (result (ref $BoxedInt)) 
    (return 
      (struct.new_with_rtt $BoxedInt 
        (i32.const 7)
        (local.get $0) 
        (rtt.canon $BoxedInt)))) 
  (func $tagged_to_int (param $0 (ref $BoxedInt)) (result i64) 
    (return 
      (struct.get $BoxedInt $val 
        (local.get $0)))) 
   (func $get_type (param $0 eqref) (result i32) 
    (if
      (ref.is_i31
        (local.get $0))
      (return
        (i32.const 1)) ;; TYPE_BOOLEAN
      (if
        (ref.is_null
          (local.get $0))
        (return
          (i32.const 0)) ;; TYPE_NIL
        (return 
          (struct.get $Any $type 
            (ref.cast 
              (ref.as_data 
                (local.get $0)) 
              (rtt.canon $Any)))))))) 
poorna2152 commented 2 years ago
  1. This program works because the param is of type $Any and $BoxedInt can be considered as a $Any.
    (func $get_type (param $0 (ref $Any)) (result i32) 
    (return 
      (struct.get $Any $type 
        (local.get $0)))))
  2. This program doesnt work because the param is of type eqref and you cannot cast a $BoxedInt to an $Any.
    (func $get_type (param $0 eqref) (result i32) 
    (return 
        (struct.get $Any $type 
          (ref.cast 
            (ref.as_data 
              (local.get $0)) 
            (rtt.canon $Any))))))

What I was doing previously was the following and it works but is considerably slow. Now assume that for that $BoxedInt is initialized as following.

       (struct.new_with_rtt $BoxedInt 
          (i32.const 0)
          (i64.const 21)
          (rtt.sub $BoxedInt
            (rtt.canon $Any)))
   (func $get_type (param $0 eqref) (result i32) 
    (return 
        (struct.get $Any $type 
            (ref.cast
                (ref.as_data
                    (local.get $0))
                (rtt.canon $Any)))))
poorna2152 commented 2 years ago

Working example:

(module 
  (type $BoxedInt (struct (field $type i32) (field $val i64)))
  (type $Any (struct (field $type i32))) 
  (global $rttAny (rtt $Any) (rtt.canon $Any))  
  (global $rttBoxedInt (rtt $BoxedInt) (rtt.sub $BoxedInt  (global.get $rttAny)))
  (import "console" "log" (func $println (param i32)))  
  (export "main" (func $main)) 
  (export "get_type" (func $get_type)) 
  (export "int_to_tagged" (func $int_to_tagged)) 
  (func $main
    (call $println
        (call $get_type
            (call $int_to_tagged
                (i64.const 21)))))
  (func $int_to_tagged (param $0 i64) (result (ref $BoxedInt)) 
    (return 
      (struct.new_with_rtt $BoxedInt 
        (i32.const 0)
        (local.get $0)
        (rtt.canon $BoxedInt)))) 
   (func $get_type (param $0 (ref $Any)) (result i32) 
    (return 
        (struct.get $Any $type 
            (local.get $0)))))