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:
llvm $ 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:
tablegen 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":
llvm %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():
cpp 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