Index: lib/Target/WebAssembly/WebAssemblyInstrMemory.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrMemory.td +++ lib/Target/WebAssembly/WebAssemblyInstrMemory.td @@ -25,9 +25,9 @@ let Defs = [ARGUMENTS] in { // Basic load. -def LOAD_I32 : I<(outs I32:$dst), (ins I32:$addr), - [(set I32:$dst, (load I32:$addr))], - "i32.load\t$dst, $addr">; +def LOAD_I32 : I<(outs I32:$dst), (ins I32:$off, I32:$addr), + [(set I32:$dst, (i32 (load (add I32:$addr, (i32 imm:$off)))))], + "i32.load\t$dst, $off($addr)">; def LOAD_I64 : I<(outs I64:$dst), (ins I32:$addr), [(set I64:$dst, (load I32:$addr))], "i64.load\t$dst, $addr">; @@ -72,6 +72,9 @@ } // Defs = [ARGUMENTS] +// Select loads with no constant offset. +def : Pat<(i32 (load I32:$addr)), (LOAD_I32 0, $addr)>; + // "Don't care" extending load become zero-extending load. def : Pat<(i32 (extloadi8 I32:$addr)), (LOAD8_U_I32 $addr)>; def : Pat<(i32 (extloadi16 I32:$addr)), (LOAD16_U_I32 $addr)>; @@ -87,8 +90,8 @@ // instruction definition patterns that don't reference all of the output // operands. // Note: WebAssembly inverts SelectionDAG's usual operand order. -def STORE_I32 : I<(outs I32:$dst), (ins I32:$addr, I32:$val), [], - "i32.store\t$dst, $addr, $val">; +def STORE_I32 : I<(outs I32:$dst), (ins I32:$off, I32:$addr, I32:$val), [], + "i32.store\t$dst, $off($addr), $val">; def STORE_I64 : I<(outs I64:$dst), (ins I32:$addr, I64:$val), [], "i64.store\t$dst, $addr, $val">; def STORE_F32 : I<(outs F32:$dst), (ins I32:$addr, F32:$val), [], @@ -98,7 +101,11 @@ } // Defs = [ARGUMENTS] -def : Pat<(store I32:$val, I32:$addr), (STORE_I32 I32:$addr, I32:$val)>; +def : Pat<(store I32:$val, I32:$addr), (STORE_I32 0, I32:$addr, I32:$val)>; +def : Pat<(store I32:$val, (add I32:$addr, (i32 imm:$off))), + (STORE_I32 imm:$off, I32:$addr, I32:$val)>; + + def : Pat<(store I64:$val, I32:$addr), (STORE_I64 I32:$addr, I64:$val)>; def : Pat<(store F32:$val, I32:$addr), (STORE_F32 I32:$addr, F32:$val)>; def : Pat<(store F64:$val, I32:$addr), (STORE_F64 I32:$addr, F64:$val)>; Index: lib/Target/WebAssembly/WebAssemblyStoreResults.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyStoreResults.cpp +++ lib/Target/WebAssembly/WebAssemblyStoreResults.cpp @@ -85,7 +85,7 @@ case WebAssembly::STORE_I32: case WebAssembly::STORE_I64: unsigned ToReg = MI.getOperand(0).getReg(); - unsigned FromReg = MI.getOperand(2).getReg(); + unsigned FromReg = MI.getOperand(3).getReg(); for (auto I = MRI.use_begin(FromReg), E = MRI.use_end(); I != E;) { MachineOperand &O = *I++; MachineInstr *Where = O.getParent(); Index: test/CodeGen/WebAssembly/cfg-stackify.ll =================================================================== --- test/CodeGen/WebAssembly/cfg-stackify.ll +++ test/CodeGen/WebAssembly/cfg-stackify.ll @@ -184,7 +184,7 @@ ; CHECK-LABEL: minimal_loop: ; CHECK-NOT: br ; CHECK: BB7_1: -; CHECK: i32.store $discard=, $0, $pop{{[0-9]+}}{{$}} +; CHECK: i32.store $discard=, 0($0), $pop{{[0-9]+}}{{$}} ; CHECK: br BB7_1{{$}} ; CHECK: BB7_2: define i32 @minimal_loop(i32* %p) { Index: test/CodeGen/WebAssembly/global.ll =================================================================== --- test/CodeGen/WebAssembly/global.ll +++ test/CodeGen/WebAssembly/global.ll @@ -11,7 +11,7 @@ ; CHECK: foo: ; CHECK: i32.const $push0=, answer{{$}} -; CHECK-NEXT: i32.load $push1=, $pop0{{$}} +; CHECK-NEXT: i32.load $push1=, 0($pop0){{$}} ; CHECK-NEXT: return $pop1{{$}} define i32 @foo() { %a = load i32, i32* @answer Index: test/CodeGen/WebAssembly/load.ll =================================================================== --- test/CodeGen/WebAssembly/load.ll +++ test/CodeGen/WebAssembly/load.ll @@ -8,7 +8,7 @@ ; CHECK-LABEL: ldi32: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} -; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: i32.load $push[[NUM:[0-9]+]]=, 0($0){{$}} ; CHECK-NEXT: return $pop[[NUM]]{{$}} define i32 @ldi32(i32 *%p) { %v = load i32, i32* %p @@ -44,3 +44,21 @@ %v = load double, double* %p ret double %v } + +; Check that a constant offset is folded into a load +; CHECK-LABEL: ldoff: +; CHECK: i32.load $push0=, 12($0) +define i32 @ldoff(i32 *%p) { + %addr = getelementptr i32, i32* %p, i32 3 + %v = load i32, i32* %addr + ret i32 %v +} + +; Check that a variable offset is not folded +; CHECK-LABEL: ldoff_var +; CHECK: i32.load $push{{[0-9]+}}=, 0($pop{{[0-9]}}) +define i32 @ldoff_var(i32 *%p, i32 %o) { + %addr = getelementptr i32, i32* %p, i32 %o + %v = load i32, i32* %addr + ret i32 %v +} \ No newline at end of file Index: test/CodeGen/WebAssembly/store-results.ll =================================================================== --- test/CodeGen/WebAssembly/store-results.ll +++ test/CodeGen/WebAssembly/store-results.ll @@ -9,7 +9,7 @@ ; CHECK-LABEL: single_block: ; CHECK-NOT: .local ; CHECK: i32.const $push{{[0-9]+}}=, 0 -; CHECK: i32.store $push[[STORE:[0-9]+]]=, $0, $pop{{[0-9]+}} +; CHECK: i32.store $push[[STORE:[0-9]+]]=, 0($0), $pop{{[0-9]+}} ; CHECK: return $pop[[STORE]]{{$}} define i32 @single_block(i32* %p) { entry: Index: test/CodeGen/WebAssembly/store.ll =================================================================== --- test/CodeGen/WebAssembly/store.ll +++ test/CodeGen/WebAssembly/store.ll @@ -7,7 +7,7 @@ ; CHECK-LABEL: sti32: ; CHECK-NEXT: .param i32, i32{{$}} -; CHECK-NEXT: i32.store $discard=, $0, $1{{$}} +; CHECK-NEXT: i32.store $discard=, 0($0), $1{{$}} ; CHECK-NEXT: return{{$}} define void @sti32(i32 *%p, i32 %v) { store i32 %v, i32* %p @@ -40,3 +40,12 @@ store double %v, double* %p ret void } + +; Check that a constant offset is folded into a store +; CHECK-LABEL: stoff: +; CHECK: i32.store $discard=, 12($0), $1 +define void @stoff(i32 *%p, i32 %v) { + %addr = getelementptr i32, i32* %p, i32 3 + store i32 %v, i32* %addr + ret void +}