diff --git a/tests/compiler/std/pointer.optimized.wat b/tests/compiler/std/pointer.optimized.wat new file mode 100644 index 00000000..6389c205 --- /dev/null +++ b/tests/compiler/std/pointer.optimized.wat @@ -0,0 +1,319 @@ +(module + (type $iii (func (param i32 i32) (result i32))) + (type $iv (func (param i32))) + (type $ii (func (param i32) (result i32))) + (type $iiiiv (func (param i32 i32 i32 i32))) + (type $v (func)) + (import "env" "abort" (func $~lib/env/abort (param i32 i32 i32 i32))) + (global $~argc (mut i32) (i32.const 0)) + (global $std/pointer/one (mut i32) (i32.const 0)) + (global $std/pointer/two (mut i32) (i32.const 0)) + (global $std/pointer/add (mut i32) (i32.const 0)) + (global $std/pointer/sub (mut i32) (i32.const 0)) + (memory $0 1) + (data (i32.const 8) "\0e\00\00\00s\00t\00d\00/\00p\00o\00i\00n\00t\00e\00r\00.\00t\00s") + (export "_setargc" (func $~setargc)) + (export "Pointer#constructor" (func $std/pointer/Pointer#constructor|trampoline)) + (export "Pointer#get:offset" (func $std/pointer/Pointer#get:offset)) + (export "Pointer#get:value" (func $std/pointer/Pointer#get:offset)) + (export "memory" (memory $0)) + (start $start) + (func $std/pointer/Pointer#constructor (; 1 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + (get_local $1) + ) + (func $std/pointer/Pointer#constructor|trampoline (; 2 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + (block $1of1 + (block $0of1 + (block $oob + (br_table $0of1 $1of1 $oob + (get_global $~argc) + ) + ) + (unreachable) + ) + (set_local $1 + (i32.const 0) + ) + ) + (call $std/pointer/Pointer#constructor + (get_local $0) + (get_local $1) + ) + ) + (func $~setargc (; 3 ;) (type $iv) (param $0 i32) + (set_global $~argc + (get_local $0) + ) + ) + (func $std/pointer/Pointer#get:offset (; 4 ;) (type $ii) (param $0 i32) (result i32) + (get_local $0) + ) + (func $start (; 5 ;) (type $v) + (set_global $std/pointer/one + (call $std/pointer/Pointer#constructor + (i32.const 0) + (i32.const 8) + ) + ) + (set_global $std/pointer/two + (call $std/pointer/Pointer#constructor + (i32.const 0) + (i32.const 24) + ) + ) + (if + (i32.ne + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/one) + ) + (i32.const 8) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 54) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.ne + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/two) + ) + (i32.const 24) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 55) + (i32.const 0) + ) + (unreachable) + ) + ) + (i32.store + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/one) + ) + (i32.const 1) + ) + (i32.store offset=4 + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/one) + ) + (i32.const 2) + ) + (if + (i32.ne + (i32.load + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/one) + ) + ) + (i32.const 1) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 59) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.ne + (i32.load offset=4 + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/one) + ) + ) + (i32.const 2) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 60) + (i32.const 0) + ) + (unreachable) + ) + ) + (set_global $std/pointer/add + (i32.add + (get_global $std/pointer/one) + (get_global $std/pointer/two) + ) + ) + (if + (i32.ne + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/add) + ) + (i32.const 32) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 63) + (i32.const 0) + ) + (unreachable) + ) + ) + (set_global $std/pointer/sub + (i32.sub + (get_global $std/pointer/two) + (get_global $std/pointer/one) + ) + ) + (if + (i32.ne + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/sub) + ) + (i32.const 16) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 66) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.ne + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/one) + ) + (i32.const 8) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 68) + (i32.const 0) + ) + (unreachable) + ) + ) + (set_global $std/pointer/one + (i32.add + (get_global $std/pointer/one) + (i32.const 8) + ) + ) + (if + (i32.ne + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/one) + ) + (i32.const 16) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 70) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.ne + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/two) + ) + (i32.const 24) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 72) + (i32.const 0) + ) + (unreachable) + ) + ) + (set_global $std/pointer/two + (i32.sub + (get_global $std/pointer/two) + (i32.const 8) + ) + ) + (set_global $std/pointer/two + (i32.sub + (get_global $std/pointer/two) + (i32.const 8) + ) + ) + (if + (i32.ne + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/two) + ) + (i32.const 8) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 75) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.ne + (i32.load + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/two) + ) + ) + (i32.const 1) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 76) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.ne + (i32.load offset=4 + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/two) + ) + ) + (i32.const 2) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 77) + (i32.const 0) + ) + (unreachable) + ) + ) + ) +) diff --git a/tests/compiler/std/pointer.ts b/tests/compiler/std/pointer.ts new file mode 100644 index 00000000..d0b95ab6 --- /dev/null +++ b/tests/compiler/std/pointer.ts @@ -0,0 +1,77 @@ +// A pointer arithmetic experiment + +export class Pointer { + + // FIXME: does not inline, always yields a trampoline + @inline constructor(offset: usize = 0) { + return changetype>(offset); + } + + // FIXME: does not inline + @inline get offset(): usize { + return changetype(this); + } + + // FIXME: does not inline + @inline get value(): T { + return changetype(changetype(this)); + } + + // FIXME: in general, inlining any of the following always yields a block. one could argue that + // this helps debuggability, or that it is unnecessary overhead due to the simplicity of the + // functions. a compromise could be to inline a block consisting of a single 'return' as is, + // where possible. + + @inline @operator("+") add(other: Pointer): Pointer { + return changetype>(changetype(this) + changetype(other)); + } + + @inline @operator("-") sub(other: Pointer): Pointer { + return changetype>(changetype(this) - changetype(other)); + } + + @inline @operator.prefix("++") inc(): Pointer { + // FIXME: this should take alignment into account, but then would require a new builtin to + // determine the minimal alignment of a struct by evaluating its field layout. + const size = isReference() ? offsetof() : sizeof(); + return changetype>(changetype(this) + size); + } + + @inline @operator.prefix("--") dec(): Pointer { + const size = isReference() ? offsetof() : sizeof(); + return changetype>(changetype(this) - size); + } +} + +@unmanaged +class Entry { + key: i32; + val: i32; +} + +var one = new Pointer(8); +var two = new Pointer(24); +assert(one.offset == 8); +assert(two.offset == 24); + +one.value.key = 1; +one.value.val = 2; +assert(one.value.key == 1); +assert(one.value.val == 2); + +var add = one + two; +assert(add.offset == 32); + +var sub = two - one; +assert(sub.offset == 16); + +assert(one.offset == 8); +++one; // FIXME: assigning to a var yields an 'auto to void' error +assert(one.offset == 16); + +assert(two.offset == 24); +--two; +--two; +assert(two.offset == 8); +assert(two.value.key == 1); +assert(two.value.val == 2); diff --git a/tests/compiler/std/pointer.untouched.wat b/tests/compiler/std/pointer.untouched.wat new file mode 100644 index 00000000..1bd577d0 --- /dev/null +++ b/tests/compiler/std/pointer.untouched.wat @@ -0,0 +1,396 @@ +(module + (type $iii (func (param i32 i32) (result i32))) + (type $iv (func (param i32))) + (type $ii (func (param i32) (result i32))) + (type $iiiiv (func (param i32 i32 i32 i32))) + (type $v (func)) + (import "env" "abort" (func $~lib/env/abort (param i32 i32 i32 i32))) + (global $~argc (mut i32) (i32.const 0)) + (global $std/pointer/one (mut i32) (i32.const 0)) + (global $std/pointer/two (mut i32) (i32.const 0)) + (global $std/pointer/add (mut i32) (i32.const 0)) + (global $std/pointer/sub (mut i32) (i32.const 0)) + (global $HEAP_BASE i32 (i32.const 40)) + (memory $0 1) + (data (i32.const 8) "\0e\00\00\00s\00t\00d\00/\00p\00o\00i\00n\00t\00e\00r\00.\00t\00s\00") + (export "_setargc" (func $~setargc)) + (export "Pointer#constructor" (func $std/pointer/Pointer#constructor|trampoline)) + (export "Pointer#get:offset" (func $std/pointer/Pointer#get:offset)) + (export "Pointer#get:value" (func $std/pointer/Pointer#get:value)) + (export "memory" (memory $0)) + (start $start) + (func $std/pointer/Pointer#constructor (; 1 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + (return + (get_local $1) + ) + ) + (func $std/pointer/Pointer#constructor|trampoline (; 2 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32) + (block $1of1 + (block $0of1 + (block $oob + (br_table $0of1 $1of1 $oob + (get_global $~argc) + ) + ) + (unreachable) + ) + (set_local $1 + (i32.const 0) + ) + ) + (call $std/pointer/Pointer#constructor + (get_local $0) + (get_local $1) + ) + ) + (func $~setargc (; 3 ;) (type $iv) (param $0 i32) + (set_global $~argc + (get_local $0) + ) + ) + (func $std/pointer/Pointer#get:offset (; 4 ;) (type $ii) (param $0 i32) (result i32) + (return + (get_local $0) + ) + ) + (func $std/pointer/Pointer#get:value (; 5 ;) (type $ii) (param $0 i32) (result i32) + (return + (get_local $0) + ) + ) + (func $start (; 6 ;) (type $v) + (local $0 i32) + (local $1 i32) + (set_global $std/pointer/one + (call $std/pointer/Pointer#constructor + (i32.const 0) + (i32.const 8) + ) + ) + (set_global $std/pointer/two + (call $std/pointer/Pointer#constructor + (i32.const 0) + (i32.const 24) + ) + ) + (if + (i32.eqz + (i32.eq + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/one) + ) + (i32.const 8) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 54) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.eqz + (i32.eq + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/two) + ) + (i32.const 24) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 55) + (i32.const 0) + ) + (unreachable) + ) + ) + (i32.store + (call $std/pointer/Pointer#get:value + (get_global $std/pointer/one) + ) + (i32.const 1) + ) + (i32.store offset=4 + (call $std/pointer/Pointer#get:value + (get_global $std/pointer/one) + ) + (i32.const 2) + ) + (if + (i32.eqz + (i32.eq + (i32.load + (call $std/pointer/Pointer#get:value + (get_global $std/pointer/one) + ) + ) + (i32.const 1) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 59) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.eqz + (i32.eq + (i32.load offset=4 + (call $std/pointer/Pointer#get:value + (get_global $std/pointer/one) + ) + ) + (i32.const 2) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 60) + (i32.const 0) + ) + (unreachable) + ) + ) + (set_global $std/pointer/add + (block $std/pointer/Pointer#add|inlined.0 (result i32) + (set_local $0 + (get_global $std/pointer/one) + ) + (set_local $1 + (get_global $std/pointer/two) + ) + (br $std/pointer/Pointer#add|inlined.0 + (i32.add + (get_local $0) + (get_local $1) + ) + ) + ) + ) + (if + (i32.eqz + (i32.eq + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/add) + ) + (i32.const 32) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 63) + (i32.const 0) + ) + (unreachable) + ) + ) + (set_global $std/pointer/sub + (block $std/pointer/Pointer#sub|inlined.0 (result i32) + (set_local $1 + (get_global $std/pointer/two) + ) + (set_local $0 + (get_global $std/pointer/one) + ) + (br $std/pointer/Pointer#sub|inlined.0 + (i32.sub + (get_local $1) + (get_local $0) + ) + ) + ) + ) + (if + (i32.eqz + (i32.eq + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/sub) + ) + (i32.const 16) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 66) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.eqz + (i32.eq + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/one) + ) + (i32.const 8) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 68) + (i32.const 0) + ) + (unreachable) + ) + ) + (set_global $std/pointer/one + (block $std/pointer/Pointer#inc|inlined.0 (result i32) + (set_local $0 + (get_global $std/pointer/one) + ) + (br $std/pointer/Pointer#inc|inlined.0 + (i32.add + (get_local $0) + (i32.const 8) + ) + ) + ) + ) + (if + (i32.eqz + (i32.eq + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/one) + ) + (i32.const 16) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 70) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.eqz + (i32.eq + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/two) + ) + (i32.const 24) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 72) + (i32.const 0) + ) + (unreachable) + ) + ) + (set_global $std/pointer/two + (block $std/pointer/Pointer#dec|inlined.0 (result i32) + (set_local $0 + (get_global $std/pointer/two) + ) + (br $std/pointer/Pointer#dec|inlined.0 + (i32.sub + (get_local $0) + (i32.const 8) + ) + ) + ) + ) + (set_global $std/pointer/two + (block $std/pointer/Pointer#dec|inlined.1 (result i32) + (set_local $0 + (get_global $std/pointer/two) + ) + (br $std/pointer/Pointer#dec|inlined.1 + (i32.sub + (get_local $0) + (i32.const 8) + ) + ) + ) + ) + (if + (i32.eqz + (i32.eq + (call $std/pointer/Pointer#get:offset + (get_global $std/pointer/two) + ) + (i32.const 8) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 75) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.eqz + (i32.eq + (i32.load + (call $std/pointer/Pointer#get:value + (get_global $std/pointer/two) + ) + ) + (i32.const 1) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 76) + (i32.const 0) + ) + (unreachable) + ) + ) + (if + (i32.eqz + (i32.eq + (i32.load offset=4 + (call $std/pointer/Pointer#get:value + (get_global $std/pointer/two) + ) + ) + (i32.const 2) + ) + ) + (block + (call $~lib/env/abort + (i32.const 0) + (i32.const 8) + (i32.const 77) + (i32.const 0) + ) + (unreachable) + ) + ) + ) +)