| Index: src/IceTargetLoweringX86BaseImpl.h
|
| diff --git a/src/IceTargetLoweringX86BaseImpl.h b/src/IceTargetLoweringX86BaseImpl.h
|
| index 99a1aeb2a8080cbf0885681a8848e190e8bbf6f0..c8bf29fad9370efce845beda24f9d1d3e7452f79 100644
|
| --- a/src/IceTargetLoweringX86BaseImpl.h
|
| +++ b/src/IceTargetLoweringX86BaseImpl.h
|
| @@ -4066,14 +4066,16 @@ inline void computeAddressOpt(Cfg *Func, const Inst *Instr, Variable *&Base,
|
| if (Func->getVMetadata()->isMultiBlock(Base) /* || Base->getUseCount() > 1*/)
|
| return;
|
|
|
| + const bool MockBounds = Func->getContext()->getFlags().getMockBoundsCheck();
|
| const VariablesMetadata *VMetadata = Func->getVMetadata();
|
| bool Continue = true;
|
| while (Continue) {
|
| const Inst *Reason = nullptr;
|
| if (matchTransitiveAssign(VMetadata, Base, Reason) ||
|
| matchTransitiveAssign(VMetadata, Index, Reason) ||
|
| - matchCombinedBaseIndex(VMetadata, Base, Index, Shift, Reason) ||
|
| - matchShiftedIndex(VMetadata, Index, Shift, Reason) ||
|
| + (!MockBounds &&
|
| + matchCombinedBaseIndex(VMetadata, Base, Index, Shift, Reason)) ||
|
| + (!MockBounds && matchShiftedIndex(VMetadata, Index, Shift, Reason)) ||
|
| matchOffsetBase(VMetadata, Base, Offset, Reason)) {
|
| dumpAddressOpt(Func, Base, Index, Shift, Offset, Reason);
|
| } else {
|
| @@ -4104,6 +4106,65 @@ inline void computeAddressOpt(Cfg *Func, const Inst *Instr, Variable *&Base,
|
| }
|
| }
|
|
|
| +/// Add a mock bounds check on the memory address before using it as a load or
|
| +/// store operand. The basic idea is that given a memory operand [reg], we
|
| +/// would first add bounds-check code something like:
|
| +///
|
| +/// cmp reg, <lb>
|
| +/// jl out_of_line_error
|
| +/// cmp reg, <ub>
|
| +/// jg out_of_line_error
|
| +///
|
| +/// In reality, the specific code will depend on how <lb> and <ub> are
|
| +/// represented, e.g. an immediate, a global, or a function argument.
|
| +///
|
| +/// As such, we need to enforce that the memory operand does not have the form
|
| +/// [reg1+reg2], because then there is no simple cmp instruction that would
|
| +/// suffice. However, we consider [reg+offset] to be OK because the offset is
|
| +/// usually small, and so <ub> could have a safety buffer built in and then we
|
| +/// could instead branch to a custom out_of_line_error that does the precise
|
| +/// check and jumps back if it turns out OK.
|
| +///
|
| +/// For the purpose of mocking the bounds check, we'll do something like this:
|
| +///
|
| +/// cmp reg, 0
|
| +/// je label
|
| +/// cmp reg, 1
|
| +/// je label
|
| +/// label:
|
| +///
|
| +/// Also note that we don't need to add a bounds check to a dereference of a
|
| +/// simple global variable address.
|
| +template <class Machine>
|
| +void TargetX86Base<Machine>::doMockBoundsCheck(Operand *Opnd) {
|
| + if (!Ctx->getFlags().getMockBoundsCheck())
|
| + return;
|
| + if (auto *Mem = llvm::dyn_cast<typename Traits::X86OperandMem>(Opnd)) {
|
| + if (Mem->getIndex()) {
|
| + llvm::report_fatal_error("doMockBoundsCheck: Opnd contains index reg");
|
| + }
|
| + Opnd = Mem->getBase();
|
| + }
|
| + // At this point Opnd could be nullptr, or Variable, or Constant, or perhaps
|
| + // something else. We only care if it is Variable.
|
| + auto *Var = llvm::dyn_cast_or_null<Variable>(Opnd);
|
| + if (Var == nullptr)
|
| + return;
|
| + // We use lowerStore() to copy out-args onto the stack. This creates a memory
|
| + // operand with the stack pointer as the base register. Don't do bounds
|
| + // checks on that.
|
| + if (Var->getRegNum() == Traits::RegisterSet::Reg_esp)
|
| + return;
|
| +
|
| + typename Traits::Insts::Label *Label =
|
| + Traits::Insts::Label::create(Func, this);
|
| + _cmp(Opnd, Ctx->getConstantZero(IceType_i32));
|
| + _br(Traits::Cond::Br_e, Label);
|
| + _cmp(Opnd, Ctx->getConstantInt32(1));
|
| + _br(Traits::Cond::Br_e, Label);
|
| + Context.insert(Label);
|
| +}
|
| +
|
| template <class Machine>
|
| void TargetX86Base<Machine>::lowerLoad(const InstLoad *Load) {
|
| // A Load instruction can be treated the same as an Assign instruction, after
|
| @@ -4114,6 +4175,7 @@ void TargetX86Base<Machine>::lowerLoad(const InstLoad *Load) {
|
| Variable *DestLoad = Load->getDest();
|
| Type Ty = DestLoad->getType();
|
| Operand *Src0 = formMemoryOperand(Load->getSourceAddress(), Ty);
|
| + doMockBoundsCheck(Src0);
|
| InstAssign *Assign = InstAssign::create(Func, DestLoad, Src0);
|
| lowerAssign(Assign);
|
| }
|
| @@ -4307,6 +4369,7 @@ void TargetX86Base<Machine>::lowerStore(const InstStore *Inst) {
|
| Operand *Addr = Inst->getAddr();
|
| typename Traits::X86OperandMem *NewAddr =
|
| formMemoryOperand(Addr, Value->getType());
|
| + doMockBoundsCheck(NewAddr);
|
| Type Ty = NewAddr->getType();
|
|
|
| if (!Traits::Is64Bit && Ty == IceType_i64) {
|
| @@ -4661,6 +4724,7 @@ void TargetX86Base<Machine>::lowerRMW(
|
| Operand *Src = RMW->getData();
|
| Type Ty = Src->getType();
|
| typename Traits::X86OperandMem *Addr = formMemoryOperand(RMW->getAddr(), Ty);
|
| + doMockBoundsCheck(Addr);
|
| if (!Traits::Is64Bit && Ty == IceType_i64) {
|
| Src = legalizeUndef(Src);
|
| Operand *SrcLo = legalize(loOperand(Src), Legal_Reg | Legal_Imm);
|
|
|