bool isPhiFrom32Def(MachineInstr *MovMI);
bool isMovFrom32Def(MachineInstr *MovMI);
bool eliminateZExtSeq(void);
+ bool eliminateZExt(void);
std::set<MachineInstr *> PhiInsns;
initialize(MF);
- return eliminateZExtSeq();
+ // First try to eliminate (zext, lshift, rshift) and then
+ // try to eliminate zext.
+ bool ZExtSeqExist, ZExtExist;
+ ZExtSeqExist = eliminateZExtSeq();
+ ZExtExist = eliminateZExt();
+ return ZExtSeqExist || ZExtExist;
}
};
return Eliminated;
}
+bool BPFMIPeephole::eliminateZExt(void) {
+ MachineInstr* ToErase = nullptr;
+ bool Eliminated = false;
+
+ for (MachineBasicBlock &MBB : *MF) {
+ for (MachineInstr &MI : MBB) {
+ // If the previous instruction was marked for elimination, remove it now.
+ if (ToErase) {
+ ToErase->eraseFromParent();
+ ToErase = nullptr;
+ }
+
+ if (MI.getOpcode() != BPF::MOV_32_64)
+ continue;
+
+ // Eliminate MOV_32_64 if possible.
+ // MOV_32_64 rA, wB
+ //
+ // If wB has been zero extended, replace it with a SUBREG_TO_REG.
+ // This is to workaround BPF programs where pkt->{data, data_end}
+ // is encoded as u32, but actually the verifier populates them
+ // as 64bit pointer. The MOV_32_64 will zero out the top 32 bits.
+ LLVM_DEBUG(dbgs() << "Candidate MOV_32_64 instruction:");
+ LLVM_DEBUG(MI.dump());
+
+ if (!isMovFrom32Def(&MI))
+ continue;
+
+ LLVM_DEBUG(dbgs() << "Removing the MOV_32_64 instruction\n");
+
+ Register dst = MI.getOperand(0).getReg();
+ Register src = MI.getOperand(1).getReg();
+
+ // Build a SUBREG_TO_REG instruction.
+ BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(BPF::SUBREG_TO_REG), dst)
+ .addImm(0).addReg(src).addImm(BPF::sub_32);
+
+ ToErase = &MI;
+ Eliminated = true;
+ }
+ }
+
+ return Eliminated;
+}
+
} // end default namespace
INITIALIZE_PASS(BPFMIPeephole, DEBUG_TYPE,
--- /dev/null
+; RUN: llc < %s -march=bpf -mattr=+alu32 -verify-machineinstrs | FileCheck %s
+;
+; Source:
+; struct __sk_buff {
+; unsigned data;
+; unsigned data_end;
+; };
+;
+; void * test(int flag, struct __sk_buff *skb)
+; {
+; void *p;
+;
+; if (flag) {
+; p = (void *)(long)skb->data;
+; __asm__ __volatile__("": : :"memory");
+; } else {
+; p = (void *)(long)skb->data_end;
+; __asm__ __volatile__("": : :"memory");
+; }
+;
+; return p;
+; }
+; Compilation flag:
+; clang -target bpf -O2 -S -emit-llvm t.c
+
+%struct.__sk_buff = type { i32, i32 }
+
+define dso_local i8* @test(i32 %flag, %struct.__sk_buff* nocapture readonly %skb) local_unnamed_addr {
+entry:
+ %tobool = icmp eq i32 %flag, 0
+ br i1 %tobool, label %if.else, label %if.then
+
+if.then:
+ %data = getelementptr inbounds %struct.__sk_buff, %struct.__sk_buff* %skb, i64 0, i32 0
+ %0 = load i32, i32* %data, align 4
+ tail call void asm sideeffect "", "~{memory}"()
+ br label %if.end
+
+if.else:
+ %data_end = getelementptr inbounds %struct.__sk_buff, %struct.__sk_buff* %skb, i64 0, i32 1
+ %1 = load i32, i32* %data_end, align 4
+ tail call void asm sideeffect "", "~{memory}"()
+ br label %if.end
+
+if.end:
+ %p.0.in.in = phi i32 [ %0, %if.then ], [ %1, %if.else ]
+ %p.0.in = zext i32 %p.0.in.in to i64
+ %p.0 = inttoptr i64 %p.0.in to i8*
+ ret i8* %p.0
+}
+
+; CHECK: w0 = *(u32 *)(r2 + 0)
+; CHECK: w0 = *(u32 *)(r2 + 4)
+; CHECK-NOT: r[[#]] = w[[#]]
+; CHECK: exit