| #
08d92ded |
| 13-Aug-2023 |
Eduard Zingerman <eddyz87@gmail.com> |
[BPF] Fix in/out argument constraints for CORE_MEM instructions
When LLVM is build with `LLVM_ENABLE_EXPENSIVE_CHECKS=ON` option the following C code snippet:
struct t { int a; } __at
[BPF] Fix in/out argument constraints for CORE_MEM instructions
When LLVM is build with `LLVM_ENABLE_EXPENSIVE_CHECKS=ON` option the following C code snippet:
struct t { int a; } __attribute__((preserve_access_index));
void test(struct t *t) { t->a = 42; }
Causes an assertion:
$ clang -g -O2 -c --target=bpf -mcpu=v2 t.c -o /dev/null
Function Live Ins: $r1 in %0
bb.0.entry: liveins: $r1 DBG_VALUE $r1, $noreg, !"t", ... %0:gpr = COPY $r1 DBG_VALUE %0:gpr, $noreg, !"t", ... %1:gpr = LD_imm64 @"llvm.t:0:0$0:0" %3:gpr = ADD_rr %0:gpr(tied-def 0), killed %1:gpr %4:gpr = MOV_ri 42 CORE_MEM killed %4:gpr, 411, %0:gpr, @"llvm.t:0:0$0:0", ... RET debug-location !25; t.c:7:1
*** Bad machine code: Explicit definition marked as use *** - function: test - basic block: %bb.0 entry (0x6210000d8a90) - instruction: CORE_MEM killed %4:gpr, 411, %0:gpr, @"llvm.t:0:0$0:0", ... - operand 0: killed %4:gpr
This happens because `CORE_MEM` instruction is defined to have output operands:
def CORE_MEM : TYPE_LD_ST<BPF_MEM.Value, BPF_W.Value, (outs GPR:$dst), (ins u64imm:$opcode, GPR:$src, u64imm:$offset), "$dst = core_mem($opcode, $src, $offset)", []>;
As documented in [1]:
> By convention, the LLVM code generator orders instruction operands > so that all register definitions come before the register uses, even > on architectures that are normally printed in other orders.
In other words, the first argument for `CORE_MEM` is considered to be a "def", while in reality it is "use":
%1:gpr = LD_imm64 @"llvm.t:0:0$0:0" %3:gpr = ADD_rr %0:gpr(tied-def 0), killed %1:gpr %4:gpr = MOV_ri 42 '---------------. v CORE_MEM killed %4:gpr, 411, %0:gpr, @"llvm.t:0:0$0:0", ...
Here is how `CORE_MEM` is constructed in `BPFMISimplifyPatchable::checkADDrr()`:
BuildMI(*DefInst->getParent(), *DefInst, DefInst->getDebugLoc(), TII->get(COREOp)) .add(DefInst->getOperand(0)).addImm(Opcode).add(*BaseOp) .addGlobalAddress(GVal);
Note that first operand is constructed as `.add(DefInst->getOperand(0))`.
For `LD{D,W,H,B}` instructions the `DefInst->getOperand(0)` is a destination register of a load, so instruction is constructed in accordance with `outs` declaration.
For `ST{D,W,H,B}` instructions the `DefInst->getOperand(0)` is a source register of a store (value to be stored), so instruction violates the `outs` declaration.
This commit fixes the issue by splitting `CORE_MEM` in three instructions: `CORE_ST`, `CORE_LD64`, `CORE_LD32` with correct `outs` specifications.
[1] https://llvm.org/docs/CodeGenerator.html#the-machineinstr-class
Differential Revision: https://reviews.llvm.org/D157806
show more ...
|