diff --git a/cranelift/docs/heapex-dyn.clif b/cranelift/docs/heapex-dyn.clif index 7ad0cd933f..276cbdd491 100644 --- a/cranelift/docs/heapex-dyn.clif +++ b/cranelift/docs/heapex-dyn.clif @@ -1,9 +1,10 @@ test verifier function %add_members(i32, i64 vmctx) -> f32 baldrdash { - gv0 = vmctx+64 - gv1 = vmctx+72 - heap0 = dynamic gv0, min 0x1000, bound gv1, guard 0 + gv0 = vmctx + gv1 = load.i64 notrap aligned gv0+64 + gv2 = load.i32 notrap aligned gv0+72 + heap0 = dynamic gv1, min 0x1000, bound gv2, guard 0 ebb0(v0: i32, v6: i64): v1 = heap_addr.i64 heap0, v0, 20 diff --git a/cranelift/docs/heapex-sm32.clif b/cranelift/docs/heapex-sm32.clif index 9ac1c8bfbd..88b78e9ea7 100644 --- a/cranelift/docs/heapex-sm32.clif +++ b/cranelift/docs/heapex-sm32.clif @@ -1,8 +1,9 @@ test verifier function %add_members(i32, i32 vmctx) -> f32 baldrdash { - gv0 = vmctx+64 - heap0 = static gv0, min 0x1000, bound 0x10_0000, guard 0x1000 + gv0 = vmctx + gv1 = load.i32 notrap aligned gv0+64 + heap0 = static gv1, min 0x1000, bound 0x10_0000, guard 0x1000 ebb0(v0: i32, v5: i32): v1 = heap_addr.i32 heap0, v0, 1 diff --git a/cranelift/docs/heapex-sm64.clif b/cranelift/docs/heapex-sm64.clif index c9057b6bb0..1c14ea6392 100644 --- a/cranelift/docs/heapex-sm64.clif +++ b/cranelift/docs/heapex-sm64.clif @@ -1,8 +1,9 @@ test verifier function %add_members(i32, i64 vmctx) -> f32 baldrdash { - gv0 = vmctx+64 - heap0 = static gv0, min 0x1000, bound 0x1_0000_0000, guard 0x8000_0000 + gv0 = vmctx + gv1 = load.i64 notrap aligned gv0+64 + heap0 = static gv1, min 0x1000, bound 0x1_0000_0000, guard 0x8000_0000 ebb0(v0: i32, v5: i64): v1 = heap_addr.i64 heap0, v0, 1 diff --git a/cranelift/docs/ir.rst b/cranelift/docs/ir.rst index 65d538d66b..775362ad69 100644 --- a/cranelift/docs/ir.rst +++ b/cranelift/docs/ir.rst @@ -562,58 +562,58 @@ to have access to a *VM context pointer* which is used as the base address. Typically, the VM context pointer is passed as a hidden function argument to Cranelift functions. -.. inst:: GV = vmctx+Offset +Chains of global value expressions are possible, but cycles are not allowed. +They will be caught by the IR verifier. - Declare a global value of the address of a field in the VM context struct. +.. inst:: GV = vmctx - This declares a global value which is a constant offset from the - VM context pointer which is passed as a hidden argument to all functions - JIT-compiled for the VM. + Declare a global value of the address of the VM context struct. - Typically, the VM context is a C struct, and the declared global value - is the address of a member of the struct. + This declares a global value which is the VM context pointer which may + be passed as a hidden argument to functions JIT-compiled for a VM. + + Typically, the VM context is a `#[repr(C, packed)]` struct. - :arg Offset: Byte offset from the VM context pointer to the global - value. :result GV: Global value. -The address of a global value can also be derived by treating another global -variable as a struct pointer. This makes it possible to chase pointers into VM -runtime data structures. +A global value can also be derived by treating another global variable as a +struct pointer and loading from one of its fields. This makes it possible to +chase pointers into VM runtime data structures. -.. inst:: GV = deref(BaseGV)+Offset +.. inst:: GV = load.Type BaseGV [Offset] - Declare a global value in a struct pointed to by BaseGV. + Declare a global value pointed to by BaseGV plus Offset, with type Type. - The address of GV can be computed by first loading a pointer from BaseGV - and adding Offset to it. - - It is assumed the BaseGV resides in accessible memory with the appropriate - alignment for storing a pointer. - - Chains of ``deref`` global values are possible, but cycles are not - allowed. They will be caught by the IR verifier. + It is assumed the BaseGV plus Offset resides in accessible memory with the + appropriate alignment for storing a value with type Type. :arg BaseGV: Global value providing the base pointer. - :arg Offset: Byte offset added to the loaded value. + :arg Offset: Offset added to the base before loading. :result GV: Global value. -.. inst:: GV = [colocated] globalsym name +.. inst:: GV = iadd_imm BaseGV, Offset - Declare a global value at a symbolic address. + Declare a global value which has the value of BaseGV offset by Offset. - The address of GV is symbolic and will be assigned a relocation, so that + :arg BaseGV: Global value providing the base value. + :arg Offset: Offset added to the base value. + +.. inst:: GV = [colocated] symbol Name + + Declare a symbolic address global value. + + The value of GV is symbolic and will be assigned a relocation, so that it can be resolved by a later linking phase. If the colocated keyword is present, the symbol's definition will be defined along with the current function, such that it can use more efficient addressing. - :arg name: External name. + :arg Name: External name. :result GV: Global value. .. autoinst:: global_value -.. autoinst:: globalsym_addr +.. autoinst:: symbol_value Heaps diff --git a/cranelift/filetests/isa/x86/binary32.clif b/cranelift/filetests/isa/x86/binary32.clif index 3f14b74c80..fec3479e41 100644 --- a/cranelift/filetests/isa/x86/binary32.clif +++ b/cranelift/filetests/isa/x86/binary32.clif @@ -12,7 +12,7 @@ function %I32() { sig0 = () fn0 = %foo() - gv0 = globalsym %some_gv + gv0 = symbol %some_gv ss0 = incoming_arg 8, offset 0 ss1 = incoming_arg 1024, offset -1024 @@ -365,9 +365,9 @@ ebb0: call_indirect sig0, v401() ; bin: stk_ovf ff d6 ; asm: movl $0, %ecx - [-,%rcx] v450 = globalsym_addr.i32 gv0 ; bin: b9 Abs4(%some_gv) 00000000 + [-,%rcx] v450 = symbol_value.i32 gv0 ; bin: b9 Abs4(%some_gv) 00000000 ; asm: movl $0, %esi - [-,%rsi] v451 = globalsym_addr.i32 gv0 ; bin: be Abs4(%some_gv) 00000000 + [-,%rsi] v451 = symbol_value.i32 gv0 ; bin: be Abs4(%some_gv) 00000000 ; Spill / Fill. diff --git a/cranelift/filetests/isa/x86/binary64-pic.clif b/cranelift/filetests/isa/x86/binary64-pic.clif index 3dec2af62b..adda09da70 100644 --- a/cranelift/filetests/isa/x86/binary64-pic.clif +++ b/cranelift/filetests/isa/x86/binary64-pic.clif @@ -15,8 +15,8 @@ function %I64() { fn0 = %foo() fn1 = colocated %bar() - gv0 = globalsym %some_gv - gv1 = globalsym colocated %some_gv + gv0 = symbol %some_gv + gv1 = symbol colocated %some_gv ; Use incoming_arg stack slots because they won't be relocated by the frame ; layout. @@ -66,18 +66,18 @@ ebb0: call_indirect sig0, v102() ; bin: stk_ovf 41 ff d2 ; asm: mov 0x0(%rip), %rcx - [-,%rcx] v3 = globalsym_addr.i64 gv0 ; bin: 48 8b 0d GOTPCRel4(%some_gv-4) 00000000 + [-,%rcx] v3 = symbol_value.i64 gv0 ; bin: 48 8b 0d GOTPCRel4(%some_gv-4) 00000000 ; asm: mov 0x0(%rip), %rsi - [-,%rsi] v4 = globalsym_addr.i64 gv0 ; bin: 48 8b 35 GOTPCRel4(%some_gv-4) 00000000 + [-,%rsi] v4 = symbol_value.i64 gv0 ; bin: 48 8b 35 GOTPCRel4(%some_gv-4) 00000000 ; asm: mov 0x0(%rip), %r10 - [-,%r10] v5 = globalsym_addr.i64 gv0 ; bin: 4c 8b 15 GOTPCRel4(%some_gv-4) 00000000 + [-,%r10] v5 = symbol_value.i64 gv0 ; bin: 4c 8b 15 GOTPCRel4(%some_gv-4) 00000000 ; asm: lea 0x0(%rip), %rcx - [-,%rcx] v6 = globalsym_addr.i64 gv1 ; bin: 48 8d 0d PCRel4(%some_gv-4) 00000000 + [-,%rcx] v6 = symbol_value.i64 gv1 ; bin: 48 8d 0d PCRel4(%some_gv-4) 00000000 ; asm: lea 0x0(%rip), %rsi - [-,%rsi] v7 = globalsym_addr.i64 gv1 ; bin: 48 8d 35 PCRel4(%some_gv-4) 00000000 + [-,%rsi] v7 = symbol_value.i64 gv1 ; bin: 48 8d 35 PCRel4(%some_gv-4) 00000000 ; asm: lea 0x0(%rip), %r10 - [-,%r10] v8 = globalsym_addr.i64 gv1 ; bin: 4c 8d 15 PCRel4(%some_gv-4) 00000000 + [-,%r10] v8 = symbol_value.i64 gv1 ; bin: 4c 8d 15 PCRel4(%some_gv-4) 00000000 return } diff --git a/cranelift/filetests/isa/x86/binary64.clif b/cranelift/filetests/isa/x86/binary64.clif index 0aac9bbafc..b50e0d33db 100644 --- a/cranelift/filetests/isa/x86/binary64.clif +++ b/cranelift/filetests/isa/x86/binary64.clif @@ -14,7 +14,7 @@ function %I64() { fn0 = %foo() fn1 = colocated %bar() - gv0 = globalsym %some_gv + gv0 = symbol %some_gv ; Use incoming_arg stack slots because they won't be relocated by the frame ; layout. @@ -518,11 +518,11 @@ ebb0: call_indirect sig0, v412() ; bin: stk_ovf 41 ff d2 ; asm: movabsq $-1, %rcx - [-,%rcx] v450 = globalsym_addr.i64 gv0 ; bin: 48 b9 Abs8(%some_gv) 0000000000000000 + [-,%rcx] v450 = symbol_value.i64 gv0 ; bin: 48 b9 Abs8(%some_gv) 0000000000000000 ; asm: movabsq $-1, %rsi - [-,%rsi] v451 = globalsym_addr.i64 gv0 ; bin: 48 be Abs8(%some_gv) 0000000000000000 + [-,%rsi] v451 = symbol_value.i64 gv0 ; bin: 48 be Abs8(%some_gv) 0000000000000000 ; asm: movabsq $-1, %r10 - [-,%r10] v452 = globalsym_addr.i64 gv0 ; bin: 49 ba Abs8(%some_gv) 0000000000000000 + [-,%r10] v452 = symbol_value.i64 gv0 ; bin: 49 ba Abs8(%some_gv) 0000000000000000 ; Spill / Fill. diff --git a/cranelift/filetests/isa/x86/legalize-heaps.clif b/cranelift/filetests/isa/x86/legalize-heaps.clif index 8fe2b8737c..df2be3f287 100644 --- a/cranelift/filetests/isa/x86/legalize-heaps.clif +++ b/cranelift/filetests/isa/x86/legalize-heaps.clif @@ -4,18 +4,18 @@ target x86_64 ; Test legalization for various forms of heap addresses. function %heap_addrs(i32, i64, i64 vmctx) { - gv0 = vmctx+64 - gv1 = vmctx+72 - gv2 = vmctx+80 - gv3 = vmctx+88 - gv4 = deref(gv3): i32 + gv4 = vmctx + gv0 = iadd_imm.i64 gv4, 64 + gv1 = iadd_imm.i64 gv4, 72 + gv2 = iadd_imm.i64 gv4, 80 + gv3 = load.i32 notrap aligned gv4+88 heap0 = static gv0, min 0x1_0000, bound 0x1_0000_0000, guard 0x8000_0000, index_type i32 heap1 = static gv0, guard 0x1000, bound 0x1_0000, index_type i32 heap2 = static gv0, min 0x1_0000, bound 0x1_0000_0000, guard 0x8000_0000, index_type i64 heap3 = static gv0, guard 0x1000, bound 0x1_0000, index_type i64 - heap4 = dynamic gv1, min 0x1_0000, bound gv4, guard 0x8000_0000, index_type i32 - heap5 = dynamic gv1, bound gv4, guard 0x1000, index_type i32 + heap4 = dynamic gv1, min 0x1_0000, bound gv3, guard 0x8000_0000, index_type i32 + heap5 = dynamic gv1, bound gv3, guard 0x1000, index_type i32 heap6 = dynamic gv1, min 0x1_0000, bound gv2, guard 0x8000_0000, index_type i64 heap7 = dynamic gv1, bound gv2, guard 0x1000, index_type i64 @@ -23,8 +23,8 @@ function %heap_addrs(i32, i64, i64 vmctx) { ; check: heap1 = static gv0, min 0, bound 0x0001_0000, guard 4096, index_type i32 ; check: heap2 = static gv0, min 0x0001_0000, bound 0x0001_0000_0000, guard 0x8000_0000, index_type i64 ; check: heap3 = static gv0, min 0, bound 0x0001_0000, guard 4096, index_type i64 - ; check: heap4 = dynamic gv1, min 0x0001_0000, bound gv4, guard 0x8000_0000, index_type i32 - ; check: heap5 = dynamic gv1, min 0, bound gv4, guard 4096, index_type i32 + ; check: heap4 = dynamic gv1, min 0x0001_0000, bound gv3, guard 0x8000_0000, index_type i32 + ; check: heap5 = dynamic gv1, min 0, bound gv3, guard 4096, index_type i32 ; check: heap6 = dynamic gv1, min 0x0001_0000, bound gv2, guard 0x8000_0000, index_type i64 ; check: heap7 = dynamic gv1, min 0, bound gv2, guard 4096, index_type i64 @@ -62,9 +62,7 @@ ebb0(v0: i32, v1: i64, v3: i64): ; check: v7 = iadd v21, v1 v8 = heap_addr.i64 heap4, v0, 0 - ; check: v27 = iadd_imm.i64 v3, 88 - ; check: v28 = load.i32 notrap aligned v27 - ; check: v22 = iadd_imm v28, 0 + ; check: v22 = load.i32 notrap aligned v3+88 ; check: v23 = iadd_imm v22, 0 ; check: v24 = icmp.i32 ugt v0, v23 ; check: brz v24, ebb4 @@ -75,37 +73,35 @@ ebb0(v0: i32, v1: i64, v3: i64): ; check: v8 = iadd v26, v25 v9 = heap_addr.i64 heap5, v0, 0 - ; check: v34 = iadd_imm.i64 v3, 88 - ; check: v35 = load.i32 notrap aligned v34 - ; check: v29 = iadd_imm v35, 0 - ; check: v30 = iadd_imm v29, 0 - ; check: v31 = icmp.i32 ugt v0, v30 - ; check: brz v31, ebb5 + ; check: v27 = load.i32 notrap aligned v3+88 + ; check: v28 = iadd_imm v27, 0 + ; check: v29 = icmp.i32 ugt v0, v28 + ; check: brz v29, ebb5 ; check: trap heap_oob ; check: ebb5: - ; check: v32 = uextend.i64 v0 - ; check: v33 = iadd_imm.i64 v3, 72 - ; check: v9 = iadd v33, v32 + ; check: v30 = uextend.i64 v0 + ; check: v31 = iadd_imm.i64 v3, 72 + ; check: v9 = iadd v31, v30 v10 = heap_addr.i64 heap6, v1, 0 + ; check: v32 = iadd_imm.i64 v3, 80 + ; check: v33 = iadd_imm v32, 0 + ; check: v34 = icmp.i64 ugt v1, v33 + ; check: brz v34, ebb6 + ; check: trap heap_oob + ; check: ebb6: + ; check: v35 = iadd_imm.i64 v3, 72 + ; check: v10 = iadd v35, v1 + + v11 = heap_addr.i64 heap7, v1, 0 ; check: v36 = iadd_imm.i64 v3, 80 ; check: v37 = iadd_imm v36, 0 ; check: v38 = icmp.i64 ugt v1, v37 - ; check: brz v38, ebb6 - ; check: trap heap_oob - ; check: ebb6: - ; check: v39 = iadd_imm.i64 v3, 72 - ; check: v10 = iadd v39, v1 - - v11 = heap_addr.i64 heap7, v1, 0 - ; check: v40 = iadd_imm.i64 v3, 80 - ; check: v41 = iadd_imm v40, 0 - ; check: v42 = icmp.i64 ugt v1, v41 - ; check: brz v42, ebb7 + ; check: brz v38, ebb7 ; check: trap heap_oob ; check: ebb7: - ; check: v43 = iadd_imm.i64 v3, 72 - ; check: v11 = iadd v43, v1 + ; check: v39 = iadd_imm.i64 v3, 72 + ; check: v11 = iadd v39, v1 return } diff --git a/cranelift/filetests/isa/x86/legalize-memory.clif b/cranelift/filetests/isa/x86/legalize-memory.clif index 3728e84b58..fbb9ca1f37 100644 --- a/cranelift/filetests/isa/x86/legalize-memory.clif +++ b/cranelift/filetests/isa/x86/legalize-memory.clif @@ -6,7 +6,8 @@ target x86_64 ; regex: EBB=ebb\d+ function %vmctx(i64 vmctx) -> i64 { - gv1 = vmctx-16 + gv0 = vmctx + gv1 = iadd_imm.i64 gv0, -16 ebb1(v1: i64): v2 = global_value.i64 gv1 @@ -15,28 +16,28 @@ ebb1(v1: i64): ; check: return v2 } -function %deref(i64 vmctx) -> i64 { - gv1 = vmctx-16 - gv2 = deref(gv1)+32: i64 +function %load(i64 vmctx) -> i64 { + gv0 = vmctx + gv1 = load.i64 notrap aligned gv0-16 + gv2 = iadd_imm.i64 gv1, 32 ebb1(v1: i64): v2 = global_value.i64 gv2 - ; check: $(a1=$V) = iadd_imm v1, -16 - ; check: $(p1=$V) = load.i64 notrap aligned $a1 + ; check: $(p1=$V) = load.i64 notrap aligned v1-16 ; check: v2 = iadd_imm $p1, 32 return v2 ; check: return v2 } -function %sym() -> i64 { - gv0 = globalsym %something - gv1 = globalsym u123:456 +function %symbol() -> i64 { + gv0 = symbol %something + gv1 = symbol u123:456 ebb1: v0 = global_value.i64 gv0 - ; check: v0 = globalsym_addr.i64 gv0 + ; check: v0 = symbol_value.i64 gv0 v1 = global_value.i64 gv1 - ; check: v1 = globalsym_addr.i64 gv1 + ; check: v1 = symbol_value.i64 gv1 v2 = bxor v0, v1 return v2 } @@ -44,8 +45,9 @@ ebb1: ; SpiderMonkey VM-style static 4+2 GB heap. ; This eliminates bounds checks completely for offsets < 2GB. function %staticheap_sm64(i32, i64 vmctx) -> f32 baldrdash { - gv0 = vmctx+64 - heap0 = static gv0, min 0x1000, bound 0x1_0000_0000, guard 0x8000_0000 + gv0 = vmctx + gv1 = iadd_imm.i64 gv0, 64 + heap0 = static gv1, min 0x1000, bound 0x1_0000_0000, guard 0x8000_0000 ebb0(v0: i32, v999: i64): ; check: ebb0( @@ -64,8 +66,9 @@ ebb0(v0: i32, v999: i64): } function %staticheap_static_oob_sm64(i32, i64 vmctx) -> f32 baldrdash { - gv0 = vmctx+64 - heap0 = static gv0, min 0x1000, bound 0x1000_0000, guard 0x8000_0000 + gv0 = vmctx + gv1 = iadd_imm.i64 gv0, 64 + heap0 = static gv1, min 0x1000, bound 0x1000_0000, guard 0x8000_0000 ebb0(v0: i32, v999: i64): ; Everything after the obviously OOB access should be eliminated, leaving @@ -87,8 +90,9 @@ ebb0(v0: i32, v999: i64): ; SpiderMonkey VM-style static 4+2 GB heap. ; Offsets >= 2 GB do require a boundscheck. function %staticheap_sm64(i32, i64 vmctx) -> f32 baldrdash { - gv0 = vmctx+64 - heap0 = static gv0, min 0x1000, bound 0x1_0000_0000, guard 0x8000_0000 + gv0 = vmctx + gv1 = iadd_imm.i64 gv0, 64 + heap0 = static gv1, min 0x1000, bound 0x1_0000_0000, guard 0x8000_0000 ebb0(v0: i32, v999: i64): ; check: ebb0( diff --git a/cranelift/filetests/isa/x86/legalize-table.clif b/cranelift/filetests/isa/x86/legalize-table.clif deleted file mode 100644 index f86913115d..0000000000 --- a/cranelift/filetests/isa/x86/legalize-table.clif +++ /dev/null @@ -1,27 +0,0 @@ -; Test legalization of tables -test legalizer -target x86_64 - -; regex: V=v\d+ -; regex: EBB=ebb\d+ - -function %test0(i64 vmctx, i64) -> i64 { - gv0 = vmctx+12 - gv1 = vmctx+14 - table0 = dynamic gv0, min 20, bound gv1, element_size 4, index_type i64 - -ebb0(v0: i64, v1: i64): - v2 = table_addr.i64 table0, v1, +3 - return v2 -} - -; check: $(bound=$V) = iadd_imm $(input=$V), 14 -; nextln: $(cond=$V) = icmp uge $(limit=$V), $bound -; nextln: brz $cond, ebb1 -; nextln: trap table_oob -; nextln: -; nextln: ebb1: -; nextln: $(base=$V) = iadd_imm.i64 $(vmctx=$V), 12 -; nextln: $(scaled=$V) = ishl_imm.i64 $(index=$V), 2 -; nextln: $(elem_addr=$V) = iadd $base, $scaled -; nextln: $(field_addr=$V) = iadd_imm $elem_addr, 3 diff --git a/cranelift/filetests/isa/x86/legalize-tables.clif b/cranelift/filetests/isa/x86/legalize-tables.clif index a9882f3cf9..5995c230e0 100644 --- a/cranelift/filetests/isa/x86/legalize-tables.clif +++ b/cranelift/filetests/isa/x86/legalize-tables.clif @@ -4,26 +4,24 @@ target x86_64 ; Test legalization for various forms of table addresses. function %table_addrs(i32, i64, i64 vmctx) { - gv0 = vmctx+72 - gv1 = vmctx+80 - gv2 = vmctx+88 - gv3 = deref(gv2): i32 + gv4 = vmctx + gv0 = iadd_imm.i64 gv4, 72 + gv1 = iadd_imm.i64 gv4, 80 + gv2 = load.i32 notrap aligned gv4+88 - table0 = dynamic gv0, min 0x1_0000, bound gv3, element_size 1, index_type i32 - table1 = dynamic gv0, bound gv3, element_size 16, index_type i32 + table0 = dynamic gv0, min 0x1_0000, bound gv2, element_size 1, index_type i32 + table1 = dynamic gv0, bound gv2, element_size 16, index_type i32 table2 = dynamic gv0, min 0x1_0000, bound gv1, element_size 1, index_type i64 table3 = dynamic gv0, bound gv1, element_size 16, index_type i64 - ; check: table0 = dynamic gv0, min 0x0001_0000, bound gv3, element_size 1, index_type i32 - ; check: table1 = dynamic gv0, min 0, bound gv3, element_size 16, index_type i32 + ; check: table0 = dynamic gv0, min 0x0001_0000, bound gv2, element_size 1, index_type i32 + ; check: table1 = dynamic gv0, min 0, bound gv2, element_size 16, index_type i32 ; check: table2 = dynamic gv0, min 0x0001_0000, bound gv1, element_size 1, index_type i64 ; check: table3 = dynamic gv0, min 0, bound gv1, element_size 16, index_type i64 ebb0(v0: i32, v1: i64, v3: i64): v4 = table_addr.i64 table0, v0, +0 - ; check: v12 = iadd_imm v3, 88 - ; check: v13 = load.i32 notrap aligned v12 - ; check: v8 = iadd_imm v13, 0 + ; check: v8 = load.i32 notrap aligned v3+88 ; check: v9 = icmp uge v0, v8 ; check: brz v9, ebb1 ; check: trap table_oob @@ -33,36 +31,34 @@ ebb0(v0: i32, v1: i64, v3: i64): ; check: v4 = iadd v11, v10 v5 = table_addr.i64 table1, v0, +0 - ; check: v19 = iadd_imm.i64 v3, 88 - ; check: v20 = load.i32 notrap aligned v19 - ; check: v14 = iadd_imm v20, 0 - ; check: v15 = icmp.i32 uge v0, v14 - ; check: brz v15, ebb2 + ; check: v12 = load.i32 notrap aligned v3+88 + ; check: v13 = icmp.i32 uge v0, v12 + ; check: brz v13, ebb2 ; check: trap table_oob ; check: ebb2: - ; check: v16 = uextend.i64 v0 - ; check: v17 = iadd_imm.i64 v3, 72 - ; check: v18 = ishl_imm v16, 4 - ; check: v5 = iadd v17, v18 + ; check: v14 = uextend.i64 v0 + ; check: v15 = iadd_imm.i64 v3, 72 + ; check: v16 = ishl_imm v14, 4 + ; check: v5 = iadd v15, v16 v6 = table_addr.i64 table2, v1, +0 - ; check: v21 = iadd_imm.i64 v3, 80 - ; check: v22 = icmp.i64 uge v1, v21 - ; check: brz v22, ebb3 + ; check: v17 = iadd_imm.i64 v3, 80 + ; check: v18 = icmp.i64 uge v1, v17 + ; check: brz v18, ebb3 ; check: trap table_oob ; check: ebb3: - ; check: v23 = iadd_imm.i64 v3, 72 - ; check: v6 = iadd v23, v1 + ; check: v19 = iadd_imm.i64 v3, 72 + ; check: v6 = iadd v19, v1 v7 = table_addr.i64 table3, v1, +0 - ; check: v24 = iadd_imm.i64 v3, 80 - ; check: v25 = icmp.i64 uge v1, v24 - ; check: brz v25, ebb4 + ; check: v20 = iadd_imm.i64 v3, 80 + ; check: v21 = icmp.i64 uge v1, v20 + ; check: brz v21, ebb4 ; check: trap table_oob ; check: ebb4: - ; check: v26 = iadd_imm.i64 v3, 72 - ; check: v27 = ishl_imm.i64 v1, 4 - ; check: v7 = iadd v26, v27 + ; check: v22 = iadd_imm.i64 v3, 72 + ; check: v23 = ishl_imm.i64 v1, 4 + ; check: v7 = iadd v22, v23 return } diff --git a/cranelift/filetests/parser/memory.clif b/cranelift/filetests/parser/memory.clif index 1c2fe4cc4c..d406007dfe 100644 --- a/cranelift/filetests/parser/memory.clif +++ b/cranelift/filetests/parser/memory.clif @@ -2,23 +2,21 @@ test cat test verifier function %vmglobal(i64 vmctx) -> i32 { - gv3 = vmctx+16 - ; check: gv3 = vmctx+16 - gv4 = vmctx+0 - ; check: gv4 = vmctx - ; not: +0 - gv5 = vmctx -256 - ; check: gv5 = vmctx-256 + gv3 = vmctx + ; check: gv3 = vmctx ebb0(v0: i64): v1 = global_value.i32 gv3 ; check: v1 = global_value.i32 gv3 return v1 } -function %deref(i64 vmctx) -> i32 { - gv3 = vmctx+16 - gv4 = deref(gv3)-32: i32 - ; check: gv4 = deref(gv3)-32 +function %load_and_add_imm(i64 vmctx) -> i32 { + gv2 = vmctx + gv3 = load.i32 notrap aligned gv2-72 + gv4 = iadd_imm.i32 gv3, -32 + ; check: gv2 = vmctx + ; check: gv3 = load.i32 notrap aligned gv2-72 + ; check: gv4 = iadd_imm.i32 gv3, -32 ebb0(v0: i64): v1 = global_value.i32 gv4 ; check: v1 = global_value.i32 gv4 @@ -27,20 +25,22 @@ ebb0(v0: i64): ; Refer to a global value before it's been declared. function %backref(i64 vmctx) -> i32 { - gv1 = deref(gv2)-32: i32 - ; check: gv1 = deref(gv2)-32 - gv2 = vmctx+16 - ; check: gv2 = vmctx+16 + gv0 = iadd_imm.i32 gv1, -32 + ; check: gv0 = iadd_imm.i32 gv1, -32 + gv1 = load.i32 notrap aligned gv2 + ; check: gv1 = load.i32 notrap aligned gv2 + gv2 = vmctx + ; check: gv2 = vmctx ebb0(v0: i64): v1 = global_value.i32 gv1 return v1 } -function %sym() -> i32 { - gv0 = globalsym %something - ; check: gv0 = globalsym %something - gv1 = globalsym u8:9 - ; check: gv1 = globalsym u8:9 +function %symbol() -> i32 { + gv0 = symbol %something + ; check: gv0 = symbol %something + gv1 = symbol u8:9 + ; check: gv1 = symbol u8:9 ebb0: v0 = global_value.i32 gv0 ; check: v0 = global_value.i32 gv0 @@ -54,7 +54,8 @@ ebb0: function %sheap(i32, i64 vmctx) -> i64 { heap1 = static gv5, min 0x1_0000, bound 0x1_0000_0000, guard 0x8000_0000 heap2 = static gv5, guard 0x1000, bound 0x1_0000 - gv5 = vmctx+64 + gv4 = vmctx + gv5 = iadd_imm.i64 gv4, 64 ; check: heap1 = static gv5, min 0x0001_0000, bound 0x0001_0000_0000, guard 0x8000_0000 ; check: heap2 = static gv5, min 0, bound 0x0001_0000, guard 4096 @@ -68,8 +69,9 @@ ebb0(v1: i32, v2: i64): function %dheap(i32, i64 vmctx) -> i64 { heap1 = dynamic gv5, min 0x1_0000, bound gv6, guard 0x8000_0000 heap2 = dynamic gv5, bound gv6, guard 0x1000 - gv5 = vmctx+64 - gv6 = vmctx+72 + gv4 = vmctx + gv5 = iadd_imm.i64 gv4, 64 + gv6 = iadd_imm.i64 gv4, 72 ; check: heap1 = dynamic gv5, min 0x0001_0000, bound gv6, guard 0x8000_0000 ; check: heap2 = dynamic gv5, min 0, bound gv6, guard 4096 diff --git a/cranelift/filetests/regalloc/coalescing-207.clif b/cranelift/filetests/regalloc/coalescing-207.clif index 08519965b6..37b028af03 100644 --- a/cranelift/filetests/regalloc/coalescing-207.clif +++ b/cranelift/filetests/regalloc/coalescing-207.clif @@ -5,7 +5,8 @@ target x86_64 haswell ; ; The coalescer creates a virtual register with two interfering values. function %pr207(i64 vmctx, i32, i32) -> i32 system_v { - gv0 = vmctx-8 + gv1 = vmctx + gv0 = iadd_imm.i64 gv1, -8 heap0 = static gv0, min 0, bound 0x5000, guard 0x0040_0000 sig0 = (i64 vmctx, i32, i32) -> i32 system_v sig1 = (i64 vmctx, i32, i32, i32) -> i32 system_v diff --git a/cranelift/filetests/regalloc/iterate.clif b/cranelift/filetests/regalloc/iterate.clif index c7ad214432..424ecfa740 100644 --- a/cranelift/filetests/regalloc/iterate.clif +++ b/cranelift/filetests/regalloc/iterate.clif @@ -104,7 +104,8 @@ ebb1(v31: i64): } function u0:26(i64 vmctx [%r14]) -> i64 [%rax] baldrdash { - gv0 = vmctx+48 + gv1 = vmctx + gv0 = iadd_imm.i64 gv1, 48 sig0 = (i32 [%rdi], i64 [%rsi], i64 vmctx [%r14], i64 sigid [%rbx]) -> i64 [%rax] baldrdash ebb0(v0: i64): diff --git a/cranelift/filetests/regalloc/reload-208.clif b/cranelift/filetests/regalloc/reload-208.clif index 0f4a2e4bb4..629d984f62 100644 --- a/cranelift/filetests/regalloc/reload-208.clif +++ b/cranelift/filetests/regalloc/reload-208.clif @@ -11,7 +11,8 @@ target x86_64 haswell ; The problem was the reload pass rewriting EBB arguments on "brnz v9, ebb3(v9)" function %pr208(i64 vmctx [%rdi]) system_v { - gv0 = vmctx-8 + gv1 = vmctx + gv0 = iadd_imm.i64 gv1, -8 heap0 = static gv0, min 0, bound 0x5000, guard 0x0040_0000 sig0 = (i64 vmctx [%rdi]) -> i32 [%rax] system_v sig1 = (i64 vmctx [%rdi], i32 [%rsi]) system_v diff --git a/cranelift/filetests/verifier/globals.clif b/cranelift/filetests/verifier/globals.clif index f5f99a95ae..4882cae2ee 100644 --- a/cranelift/filetests/verifier/globals.clif +++ b/cranelift/filetests/verifier/globals.clif @@ -1,17 +1,17 @@ test verifier target x86_64 -function %deref_base_type(i64 vmctx) { - gv0 = vmctx+0 - gv1 = deref(gv0): i32 - gv2 = deref(gv1): i32 ; error: deref base gv1 has type i32, which is not the pointer type i64 +function %load_base_type(i64 vmctx) { + gv0 = vmctx + gv1 = load.i32 notrap aligned gv0 + gv2 = load.i32 notrap aligned gv1 ; error: base gv1 has type i32, which is not the pointer type i64 ebb0(v0: i64): return } function %global_value_wrong_type(i64 vmctx) { - gv0 = vmctx+0 + gv0 = vmctx ebb0(v0: i64): v1 = global_value.i32 gv0 ; error: global_value instruction with type i32 references global value with type i64 diff --git a/cranelift/filetests/verifier/heap.clif b/cranelift/filetests/verifier/heap.clif index 68d13e66f2..a436c623d8 100644 --- a/cranelift/filetests/verifier/heap.clif +++ b/cranelift/filetests/verifier/heap.clif @@ -2,8 +2,8 @@ test verifier target x86_64 function %heap_base_type(i64 vmctx) { - gv0 = vmctx+0 - gv1 = deref(gv0): i32 + gv0 = vmctx + gv1 = load.i32 notrap aligned gv0 heap0 = static gv1, guard 0x1000, bound 0x1_0000, index_type i32 ; error: heap base has type i32, which is not the pointer type i64 ebb0(v0: i64): @@ -11,7 +11,7 @@ ebb0(v0: i64): } function %invalid_base(i64 vmctx) { - gv0 = vmctx+0 + gv0 = vmctx heap0 = dynamic gv1, bound gv0, guard 0x1000, index_type i64 ; error: invalid base global value gv1 ebb0(v0: i64): @@ -19,7 +19,7 @@ ebb0(v0: i64): } function %invalid_bound(i64 vmctx) { - gv0 = vmctx+0 + gv0 = vmctx heap0 = dynamic gv0, bound gv1, guard 0x1000, index_type i64 ; error: invalid bound global value gv1 ebb0(v0: i64): @@ -27,8 +27,8 @@ ebb0(v0: i64): } function %heap_bound_type(i64 vmctx) { - gv0 = vmctx+0 - gv1 = deref(gv0): i16 + gv0 = vmctx + gv1 = load.i16 notrap aligned gv0 heap0 = dynamic gv0, bound gv1, guard 0x1000, index_type i32 ; error: heap index type i32 differs from the type of its bound, i16 ebb0(v0: i64): @@ -36,7 +36,7 @@ ebb0(v0: i64): } function %heap_addr_index_type(i64 vmctx, i64) { - gv0 = vmctx+0 + gv0 = vmctx heap0 = static gv0, guard 0x1000, bound 0x1_0000, index_type i32 ebb0(v0: i64, v1: i64): diff --git a/cranelift/filetests/verifier/memory.clif b/cranelift/filetests/verifier/memory.clif index a0bcaa11a1..cbaddcb13b 100644 --- a/cranelift/filetests/verifier/memory.clif +++ b/cranelift/filetests/verifier/memory.clif @@ -1,15 +1,15 @@ test verifier -function %deref_cycle() { - gv1 = deref(gv2)-32: i32 ; error: deref cycle: [gv1, gv2] - gv2 = deref(gv1): i32 +function %cycle() { + gv0 = load.i32 notrap aligned gv1 ; error: global value cycle: [gv0, gv1] + gv1 = load.i32 notrap aligned gv0-32 ebb1: return } function %self_cycle() { - gv0 = deref(gv0)-32: i32 ; error: deref cycle: [gv0] + gv0 = load.i32 notrap aligned gv0 ; error: global value cycle: [gv0] ebb1: return diff --git a/cranelift/filetests/verifier/table.clif b/cranelift/filetests/verifier/table.clif index 10fdaaf87f..7502e00044 100644 --- a/cranelift/filetests/verifier/table.clif +++ b/cranelift/filetests/verifier/table.clif @@ -2,8 +2,8 @@ test verifier target x86_64 function %table_base_type(i64 vmctx) { - gv0 = vmctx+0 - gv1 = deref(gv0): i32 + gv0 = vmctx + gv1 = load.i32 notrap aligned gv0 table0 = dynamic gv1, element_size 1, bound gv1, index_type i32 ; error: table base has type i32, which is not the pointer type i64 ebb0(v0: i64): @@ -11,7 +11,7 @@ ebb0(v0: i64): } function %invalid_base(i64 vmctx) { - gv0 = vmctx+0 + gv0 = vmctx table0 = dynamic gv1, bound gv0, element_size 1, index_type i64 ; error: invalid base global value gv1 ebb0(v0: i64): @@ -19,7 +19,7 @@ ebb0(v0: i64): } function %invalid_bound(i64 vmctx) { - gv0 = vmctx+0 + gv0 = vmctx table0 = dynamic gv0, bound gv1, element_size 1, index_type i64 ; error: invalid bound global value gv1 ebb0(v0: i64): @@ -27,8 +27,8 @@ ebb0(v0: i64): } function %table_bound_type(i64 vmctx) { - gv0 = vmctx+0 - gv1 = deref(gv0): i16 + gv0 = vmctx + gv1 = load.i16 notrap aligned gv0 table0 = dynamic gv0, bound gv1, element_size 1, index_type i32 ; error: table index type i32 differs from the type of its bound, i16 ebb0(v0: i64): @@ -36,8 +36,8 @@ ebb0(v0: i64): } function %table_addr_index_type(i64 vmctx, i64) { - gv0 = vmctx+0 - gv1 = deref(gv0): i32 + gv0 = vmctx + gv1 = load.i32 notrap aligned gv0 table0 = dynamic gv0, element_size 1, bound gv1, index_type i32 ebb0(v0: i64, v1: i64): diff --git a/cranelift/filetests/verifier/undeclared_vmctx.clif b/cranelift/filetests/verifier/undeclared_vmctx.clif index 47f1192569..1b760b3ff3 100644 --- a/cranelift/filetests/verifier/undeclared_vmctx.clif +++ b/cranelift/filetests/verifier/undeclared_vmctx.clif @@ -2,7 +2,7 @@ test verifier ; Using a vmctx global value without declaring it first leads to an error. function %vmglobal_err(i64) -> i64 { - gv4 = vmctx+0 ; error: undeclared vmctx reference + gv4 = vmctx ; error: undeclared vmctx reference ebb0(v0: i64): v1 = global_value.i64 gv4 return v1 @@ -10,7 +10,7 @@ ebb0(v0: i64): ; If it is declared, all is fine. function %vmglobal_ok(i64 vmctx) -> i64 { - gv4 = vmctx+0 + gv4 = vmctx ebb0(v0: i64): v1 = global_value.i64 gv4 return v1 diff --git a/lib/codegen/meta-python/base/instructions.py b/lib/codegen/meta-python/base/instructions.py index 9f7caf5c48..0b48fb84f2 100644 --- a/lib/codegen/meta-python/base/instructions.py +++ b/lib/codegen/meta-python/base/instructions.py @@ -506,9 +506,9 @@ global_value = Instruction( # A specialized form of global_value instructions that only handles # symbolic names. -globalsym_addr = Instruction( - 'globalsym_addr', r""" - Compute the address of global GV, which is a symbolic name. +symbol_value = Instruction( + 'symbol_value', r""" + Compute the value of global GV, which is a symbolic address. """, ins=GV, outs=addr) diff --git a/lib/codegen/meta-python/isa/x86/encodings.py b/lib/codegen/meta-python/isa/x86/encodings.py index 1e406fb8fa..a5101261c6 100644 --- a/lib/codegen/meta-python/isa/x86/encodings.py +++ b/lib/codegen/meta-python/isa/x86/encodings.py @@ -428,18 +428,18 @@ X86_64.enc(base.func_addr.i64, *r.got_fnaddr8.rex(0x8b, w=1), # # Non-PIC -X86_32.enc(base.globalsym_addr.i32, *r.gvaddr4(0xb8), +X86_32.enc(base.symbol_value.i32, *r.gvaddr4(0xb8), isap=Not(is_pic)) -X86_64.enc(base.globalsym_addr.i64, *r.gvaddr8.rex(0xb8, w=1), +X86_64.enc(base.symbol_value.i64, *r.gvaddr8.rex(0xb8, w=1), isap=Not(is_pic)) # PIC, colocated -X86_64.enc(base.globalsym_addr.i64, *r.pcrel_gvaddr8.rex(0x8d, w=1), +X86_64.enc(base.symbol_value.i64, *r.pcrel_gvaddr8.rex(0x8d, w=1), isap=is_pic, instp=IsColocatedData()) # PIC, non-colocated -X86_64.enc(base.globalsym_addr.i64, *r.got_gvaddr8.rex(0x8b, w=1), +X86_64.enc(base.symbol_value.i64, *r.got_gvaddr8.rex(0x8b, w=1), isap=is_pic) # diff --git a/lib/codegen/src/ir/globalvalue.rs b/lib/codegen/src/ir/globalvalue.rs index 8a119bce51..97ebdae262 100644 --- a/lib/codegen/src/ir/globalvalue.rs +++ b/lib/codegen/src/ir/globalvalue.rs @@ -1,6 +1,6 @@ //! Global values. -use ir::immediates::Offset32; +use ir::immediates::{Imm64, Offset32}; use ir::{ExternalName, GlobalValue, Type}; use isa::TargetIsa; use std::fmt; @@ -8,35 +8,47 @@ use std::fmt; /// Information about a global value declaration. #[derive(Clone)] pub enum GlobalValueData { - /// Value is the address of a field in the VM context struct, a constant offset from the VM - /// context pointer. - VMContext { - /// Offset from the `vmctx` pointer. - offset: Offset32, - }, + /// Value is the address of the VM context struct. + VMContext, /// Value is pointed to by another global value. /// /// The `base` global value is assumed to contain a pointer. This global value is computed - /// by loading from memory at that pointer value, and then adding an offset. The memory must - /// be accessible, and naturally aligned to hold a value of the type. - Deref { + /// by loading from memory at that pointer value. The memory must be accessible, and + /// naturally aligned to hold a value of the type. + Load { /// The base pointer global value. base: GlobalValue, - /// Byte offset to be added to the loaded value. + /// Offset added to the base pointer before doing the load. offset: Offset32, /// Type of the loaded value. - memory_type: Type, + global_type: Type, }, - /// Value is identified by a symbolic name. Cranelift itself does not interpret this name; + /// Value is an offset from another global value. + IAddImm { + /// The base pointer global value. + base: GlobalValue, + + /// Byte offset to be added to the value. + offset: Imm64, + + /// Type of the iadd. + global_type: Type, + }, + + /// Value is a symbolic address. Cranelift itself does not interpret this name; /// it's used by embedders to link with other data structures. - Sym { + Symbol { /// The symbolic name. name: ExternalName, + /// Offset from the symbol. This can be used instead of IAddImm to represent folding an + /// offset into a symbol. + offset: Imm64, + /// Will this symbol be defined nearby, such that it will always be a certain distance /// away, after linking? If so, references to it can avoid going through a GOT. Note that /// symbols meant to be preemptible cannot be colocated. @@ -45,10 +57,10 @@ pub enum GlobalValueData { } impl GlobalValueData { - /// Assume that `self` is an `GlobalValueData::Sym` and return its name. + /// Assume that `self` is an `GlobalValueData::Symbol` and return its name. pub fn symbol_name(&self) -> &ExternalName { match *self { - GlobalValueData::Sym { ref name, .. } => name, + GlobalValueData::Symbol { ref name, .. } => name, _ => panic!("only symbols have names"), } } @@ -56,8 +68,11 @@ impl GlobalValueData { /// Return the type of this global. pub fn global_type(&self, isa: &TargetIsa) -> Type { match *self { - GlobalValueData::VMContext { .. } | GlobalValueData::Sym { .. } => isa.pointer_type(), - GlobalValueData::Deref { memory_type, .. } => memory_type, + GlobalValueData::VMContext { .. } | GlobalValueData::Symbol { .. } => { + isa.pointer_type() + } + GlobalValueData::IAddImm { global_type, .. } + | GlobalValueData::Load { global_type, .. } => global_type, } } } @@ -65,20 +80,34 @@ impl GlobalValueData { impl fmt::Display for GlobalValueData { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - GlobalValueData::VMContext { offset } => write!(f, "vmctx{}", offset), - GlobalValueData::Deref { + GlobalValueData::VMContext => write!(f, "vmctx"), + GlobalValueData::Load { base, offset, - memory_type, - } => write!(f, "deref({}){}: {}", base, offset, memory_type), - GlobalValueData::Sym { + global_type, + } => write!(f, "load.{} notrap aligned {}{}", global_type, base, offset), + GlobalValueData::IAddImm { + global_type, + base, + offset, + } => write!(f, "iadd_imm.{} {}, {}", global_type, base, offset), + GlobalValueData::Symbol { ref name, + offset, colocated, } => { if colocated { write!(f, "colocated ")?; } - write!(f, "globalsym {}", name) + write!(f, "symbol {}", name)?; + let offset_val: i64 = offset.into(); + if offset_val > 0 { + write!(f, "+")?; + } + if offset_val != 0 { + write!(f, "{}", offset)?; + } + Ok(()) } } } diff --git a/lib/codegen/src/ir/immediates.rs b/lib/codegen/src/ir/immediates.rs index f61fce2196..3297769ac4 100644 --- a/lib/codegen/src/ir/immediates.rs +++ b/lib/codegen/src/ir/immediates.rs @@ -214,6 +214,16 @@ impl Offset32 { pub fn new(x: i32) -> Self { Offset32(x) } + + /// Create a new `Offset32` representing the signed numver `x` if possible. + pub fn try_from_i64(x: i64) -> Option { + let casted = x as i32; + if casted as i64 == x { + Some(Self::new(casted)) + } else { + None + } + } } impl Into for Offset32 { diff --git a/lib/codegen/src/legalizer/globalvalue.rs b/lib/codegen/src/legalizer/globalvalue.rs index 61723af0d7..352c03e13b 100644 --- a/lib/codegen/src/legalizer/globalvalue.rs +++ b/lib/codegen/src/legalizer/globalvalue.rs @@ -28,54 +28,99 @@ pub fn expand_global_value( }; match func.global_values[gv] { - ir::GlobalValueData::VMContext { offset } => vmctx_addr(inst, func, offset.into()), - ir::GlobalValueData::Deref { + ir::GlobalValueData::VMContext => vmctx_addr(inst, func), + ir::GlobalValueData::IAddImm { base, offset, - memory_type, - } => deref_addr(inst, func, base, offset.into(), memory_type, isa), - ir::GlobalValueData::Sym { .. } => globalsym(inst, func, gv, isa), + global_type, + } => iadd_imm_addr(inst, func, base, offset.into(), global_type), + ir::GlobalValueData::Load { + base, + offset, + global_type, + } => load_addr(inst, func, base, offset, global_type, isa), + ir::GlobalValueData::Symbol { .. } => symbol(inst, func, gv, isa), } } /// Expand a `global_value` instruction for a vmctx global. -fn vmctx_addr(inst: ir::Inst, func: &mut ir::Function, offset: i64) { +fn vmctx_addr(inst: ir::Inst, func: &mut ir::Function) { // Get the value representing the `vmctx` argument. let vmctx = func .special_param(ir::ArgumentPurpose::VMContext) .expect("Missing vmctx parameter"); - // Simply replace the `global_value` instruction with an `iadd_imm`, reusing the result value. - func.dfg.replace(inst).iadd_imm(vmctx, offset); + // Replace the `global_value` instruction's value with an alias to the vmctx arg. + let result = func.dfg.first_result(inst); + func.dfg.clear_results(inst); + func.dfg.change_to_alias(result, vmctx); + func.layout.remove_inst(inst); } -/// Expand a `global_value` instruction for a deref global. -fn deref_addr( +/// Expand a `global_value` instruction for an iadd_imm global. +fn iadd_imm_addr( inst: ir::Inst, func: &mut ir::Function, base: ir::GlobalValue, offset: i64, - memory_type: ir::Type, + global_type: ir::Type, +) { + let mut pos = FuncCursor::new(func).at_inst(inst); + + // Get the value for the lhs. For tidiness, expand VMContext here so that we avoid + // `vmctx_addr` which creates an otherwise unneeded value alias. + let lhs = if let ir::GlobalValueData::VMContext = pos.func.global_values[base] { + pos.func + .special_param(ir::ArgumentPurpose::VMContext) + .expect("Missing vmctx parameter") + } else { + pos.ins().global_value(global_type, base) + }; + + // Simply replace the `global_value` instruction with an `iadd_imm`, reusing the result value. + pos.func.dfg.replace(inst).iadd_imm(lhs, offset); +} + +/// Expand a `global_value` instruction for a load global. +fn load_addr( + inst: ir::Inst, + func: &mut ir::Function, + base: ir::GlobalValue, + offset: ir::immediates::Offset32, + global_type: ir::Type, isa: &TargetIsa, ) { // We need to load a pointer from the `base` global value, so insert a new `global_value` // instruction. This depends on the iterative legalization loop. Note that the IR verifier - // detects any cycles in the `deref` globals. + // detects any cycles in the `load` globals. let ptr_ty = isa.pointer_type(); let mut pos = FuncCursor::new(func).at_inst(inst); pos.use_srcloc(inst); - let base_addr = pos.ins().global_value(ptr_ty, base); + // Get the value for the base. For tidiness, expand VMContext here so that we avoid + // `vmctx_addr` which creates an otherwise unneeded value alias. + let base_addr = if let ir::GlobalValueData::VMContext = pos.func.global_values[base] { + pos.func + .special_param(ir::ArgumentPurpose::VMContext) + .expect("Missing vmctx parameter") + } else { + pos.ins().global_value(ptr_ty, base) + }; + + // Global-value loads are always notrap and aligned. let mut mflags = ir::MemFlags::new(); - // Deref globals are required to be accessible and aligned. mflags.set_notrap(); mflags.set_aligned(); - let loaded = pos.ins().load(memory_type, mflags, base_addr, 0); - pos.func.dfg.replace(inst).iadd_imm(loaded, offset); + + // Perform the load. + pos.func + .dfg + .replace(inst) + .load(global_type, mflags, base_addr, offset); } /// Expand a `global_value` instruction for a symbolic name global. -fn globalsym(inst: ir::Inst, func: &mut ir::Function, gv: ir::GlobalValue, isa: &TargetIsa) { +fn symbol(inst: ir::Inst, func: &mut ir::Function, gv: ir::GlobalValue, isa: &TargetIsa) { let ptr_ty = isa.pointer_type(); - func.dfg.replace(inst).globalsym_addr(ptr_ty, gv); + func.dfg.replace(inst).symbol_value(ptr_ty, gv); } diff --git a/lib/codegen/src/predicates.rs b/lib/codegen/src/predicates.rs index 58ae0a40fb..eca8f2318c 100644 --- a/lib/codegen/src/predicates.rs +++ b/lib/codegen/src/predicates.rs @@ -55,7 +55,7 @@ pub fn is_colocated_func(func_ref: ir::FuncRef, func: &ir::Function) -> bool { #[allow(dead_code)] pub fn is_colocated_data(global_value: ir::GlobalValue, func: &ir::Function) -> bool { match func.global_values[global_value] { - ir::GlobalValueData::Sym { colocated, .. } => colocated, + ir::GlobalValueData::Symbol { colocated, .. } => colocated, _ => panic!("is_colocated_data only makes sense for data with symbolic addresses"), } } diff --git a/lib/codegen/src/verifier/mod.rs b/lib/codegen/src/verifier/mod.rs index 33ec402449..16e21565ac 100644 --- a/lib/codegen/src/verifier/mod.rs +++ b/lib/codegen/src/verifier/mod.rs @@ -43,7 +43,7 @@ //! //! Global values //! -//! - Detect cycles in deref(base) declarations. +//! - Detect cycles in global values. //! - Detect use of 'vmctx' global value when no corresponding parameter is defined. //! //! TODO: @@ -336,16 +336,28 @@ impl<'a> Verifier<'a> { seen.insert(gv); let mut cur = gv; - while let ir::GlobalValueData::Deref { base, .. } = self.func.global_values[cur] { - if seen.insert(base).is_some() { - if !cycle_seen { - report!(errors, gv, "deref cycle: {}", DisplayList(seen.as_slice())); - cycle_seen = true; // ensures we don't report the cycle multiple times - } - continue 'gvs; - } + loop { + match self.func.global_values[cur] { + ir::GlobalValueData::Load { base, .. } + | ir::GlobalValueData::IAddImm { base, .. } => { + if seen.insert(base).is_some() { + if !cycle_seen { + report!( + errors, + gv, + "global value cycle: {}", + DisplayList(seen.as_slice()) + ); + // ensures we don't report the cycle multiple times + cycle_seen = true; + } + continue 'gvs; + } - cur = base; + cur = base; + } + _ => break, + } } match self.func.global_values[gv] { @@ -358,7 +370,30 @@ impl<'a> Verifier<'a> { report!(errors, gv, "undeclared vmctx reference {}", gv); } } - ir::GlobalValueData::Deref { base, .. } => { + ir::GlobalValueData::IAddImm { + base, global_type, .. + } => { + if !global_type.is_int() { + report!( + errors, + gv, + "iadd_imm global value with non-int type {}", + global_type + ); + } else if let Some(isa) = self.isa { + let base_type = self.func.global_values[base].global_type(isa); + if global_type != base_type { + report!( + errors, + gv, + "iadd_imm type {} differs from operand type {}", + global_type, + base_type + ); + } + } + } + ir::GlobalValueData::Load { base, .. } => { if let Some(isa) = self.isa { let base_type = self.func.global_values[base].global_type(isa); let pointer_type = isa.pointer_type(); @@ -366,7 +401,7 @@ impl<'a> Verifier<'a> { report!( errors, gv, - "deref base {} has type {}, which is not the pointer type {}", + "base {} has type {}, which is not the pointer type {}", base, base_type, pointer_type diff --git a/lib/module/src/module.rs b/lib/module/src/module.rs index 91faef517e..909fa36c6d 100644 --- a/lib/module/src/module.rs +++ b/lib/module/src/module.rs @@ -462,8 +462,9 @@ where pub fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue { let decl = &self.contents.data_objects[data].decl; let colocated = decl.linkage.is_final(); - func.create_global_value(ir::GlobalValueData::Sym { + func.create_global_value(ir::GlobalValueData::Symbol { name: ir::ExternalName::user(1, data.index() as u32), + offset: ir::immediates::Imm64::new(0), colocated, }) } diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 3003f037d9..5358109a2e 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -164,8 +164,9 @@ impl<'a> Context<'a> { fn add_gv(&mut self, gv: GlobalValue, data: GlobalValueData, loc: Location) -> ParseResult<()> { self.map.def_gv(gv, loc)?; while self.function.global_values.next_key().index() <= gv.index() { - self.function.create_global_value(GlobalValueData::Sym { + self.function.create_global_value(GlobalValueData::Symbol { name: ExternalName::testcase(""), + offset: Imm64::new(0), colocated: false, }); } @@ -590,14 +591,32 @@ impl<'a> Parser<'a> { // present, it must contain a sign. fn optional_offset32(&mut self) -> ParseResult { if let Some(Token::Integer(text)) = self.token() { - self.consume(); - // Lexer just gives us raw text that looks like an integer. - // Parse it as an `Offset32` to check for overflow and other issues. - text.parse().map_err(|e| self.error(e)) - } else { - // An offset32 operand can be absent. - Ok(Offset32::new(0)) + if text.starts_with('+') || text.starts_with('-') { + self.consume(); + // Lexer just gives us raw text that looks like an integer. + // Parse it as an `Offset32` to check for overflow and other issues. + return text.parse().map_err(|e| self.error(e)); + } } + // An offset32 operand can be absent. + Ok(Offset32::new(0)) + } + + // Match and consume an optional offset32 immediate. + // + // Note that this will match an empty string as an empty offset, and that if an offset is + // present, it must contain a sign. + fn optional_offset_imm64(&mut self) -> ParseResult { + if let Some(Token::Integer(text)) = self.token() { + if text.starts_with('+') || text.starts_with('-') { + self.consume(); + // Lexer just gives us raw text that looks like an integer. + // Parse it as an `Offset32` to check for overflow and other issues. + return text.parse().map_err(|e| self.error(e)); + } + } + // If no explicit offset is present, the offset is 0. + Ok(Imm64::new(0)) } // Match and consume an Ieee32 immediate. @@ -1185,9 +1204,10 @@ impl<'a> Parser<'a> { // Parse a global value decl. // // global-val-decl ::= * GlobalValue(gv) "=" global-val-desc - // global-val-desc ::= "vmctx" offset32 - // | "deref" "(" GlobalValue(base) ")" offset32 - // | globalsym ["colocated"] name + // global-val-desc ::= "vmctx" + // | "load" "." type "notrap" "aligned" GlobalValue(base) [offset] + // | "iadd_imm" "(" GlobalValue(base) ")" imm64 + // | "symbol" ["colocated"] name + imm64 // fn parse_global_value_decl(&mut self) -> ParseResult<(GlobalValue, GlobalValueData)> { let gv = self.match_gv("expected global value number: gv«n»")?; @@ -1195,27 +1215,55 @@ impl<'a> Parser<'a> { self.match_token(Token::Equal, "expected '=' in global value declaration")?; let data = match self.match_any_identifier("expected global value kind")? { - "vmctx" => { - let offset = self.optional_offset32()?; - GlobalValueData::VMContext { offset } - } - "deref" => { - self.match_token(Token::LPar, "expected '(' in 'deref' global value decl")?; + "vmctx" => GlobalValueData::VMContext, + "load" => { + self.match_token( + Token::Dot, + "expected '.' followed by type in load global value decl", + )?; + let global_type = self.match_type("expected load type")?; + let flags = self.optional_memflags(); let base = self.match_gv("expected global value: gv«n»")?; - self.match_token(Token::RPar, "expected ')' in 'deref' global value decl")?; let offset = self.optional_offset32()?; - self.match_token(Token::Colon, "expected ':' in 'deref' global value decl")?; - let memory_type = self.match_type("expected deref type")?; - GlobalValueData::Deref { + let mut expected_flags = MemFlags::new(); + expected_flags.set_notrap(); + expected_flags.set_aligned(); + if flags != expected_flags { + return err!(self.loc, "global-value load must be notrap and aligned"); + } + GlobalValueData::Load { base, offset, - memory_type, + global_type, } } - "globalsym" => { + "iadd_imm" => { + self.match_token( + Token::Dot, + "expected '.' followed by type in iadd_imm global value decl", + )?; + let global_type = self.match_type("expected iadd type")?; + let base = self.match_gv("expected global value: gv«n»")?; + self.match_token( + Token::Comma, + "expected ',' followed by rhs in iadd_imm global value decl", + )?; + let offset = self.match_imm64("expected iadd_imm immediate")?; + GlobalValueData::IAddImm { + base, + offset, + global_type, + } + } + "symbol" => { let colocated = self.optional(Token::Identifier("colocated")); let name = self.parse_external_name()?; - GlobalValueData::Sym { name, colocated } + let offset = self.optional_offset_imm64()?; + GlobalValueData::Symbol { + name, + offset, + colocated, + } } other => return err!(self.loc, "Unknown global value kind '{}'", other), }; @@ -2680,8 +2728,8 @@ mod tests { fn duplicate_gv() { let ParseError { location, message } = Parser::new( "function %ebbs() system_v { - gv0 = vmctx+64 - gv0 = vmctx+64", + gv0 = vmctx + gv0 = vmctx", ).parse_function(None) .unwrap_err(); diff --git a/lib/wasm/src/environ/dummy.rs b/lib/wasm/src/environ/dummy.rs index e0c972c27d..d7c9c47c10 100644 --- a/lib/wasm/src/environ/dummy.rs +++ b/lib/wasm/src/environ/dummy.rs @@ -2,7 +2,7 @@ //! wasm translation. use cranelift_codegen::cursor::FuncCursor; -use cranelift_codegen::ir::immediates::Imm64; +use cranelift_codegen::ir::immediates::{Imm64, Offset32}; use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::{self, InstBuilder}; use cranelift_codegen::settings; @@ -162,21 +162,26 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable { // Just create a dummy `vmctx` global. - let offset = ((index * 8) as i32 + 8).into(); - let gv = func.create_global_value(ir::GlobalValueData::VMContext { offset }); + let offset = ((index * 8) as i64 + 8).into(); + let vmctx = func.create_global_value(ir::GlobalValueData::VMContext {}); + let iadd = func.create_global_value(ir::GlobalValueData::IAddImm { + base: vmctx, + offset, + global_type: self.pointer_type(), + }); GlobalVariable::Memory { - gv, + gv: iadd, ty: self.mod_info.globals[index].entity.ty, } } fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap { // Create a static heap whose base address is stored at `vmctx+0`. - let addr = func.create_global_value(ir::GlobalValueData::VMContext { offset: 0.into() }); - let gv = func.create_global_value(ir::GlobalValueData::Deref { + let addr = func.create_global_value(ir::GlobalValueData::VMContext); + let gv = func.create_global_value(ir::GlobalValueData::Load { base: addr, - offset: 0.into(), - memory_type: self.pointer_type(), + offset: Offset32::new(0), + global_type: self.pointer_type(), }); func.create_heap(ir::HeapData { @@ -192,19 +197,16 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> ir::Table { // Create a table whose base address is stored at `vmctx+0`. - let base_gv_addr = - func.create_global_value(ir::GlobalValueData::VMContext { offset: 0.into() }); - let base_gv = func.create_global_value(ir::GlobalValueData::Deref { - base: base_gv_addr, - offset: 0.into(), - memory_type: self.pointer_type(), + let vmctx = func.create_global_value(ir::GlobalValueData::VMContext); + let base_gv = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: Offset32::new(0), + global_type: self.pointer_type(), }); - let bound_gv_addr = - func.create_global_value(ir::GlobalValueData::VMContext { offset: 0.into() }); - let bound_gv = func.create_global_value(ir::GlobalValueData::Deref { - base: bound_gv_addr, - offset: 0.into(), - memory_type: self.pointer_type(), + let bound_gv = func.create_global_value(ir::GlobalValueData::Load { + base: vmctx, + offset: Offset32::new(0), + global_type: self.pointer_type(), }); func.create_table(ir::TableData {