| // |
| // The Subzero Code Generator |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// \brief Implements the TargetLoweringMIPS32 class, which consists almost |
| /// entirely of the lowering sequence for each high-level instruction. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "IceTargetLoweringMIPS32.h" |
| |
| #include "IceCfg.h" |
| #include "IceCfgNode.h" |
| #include "IceClFlags.h" |
| #include "IceDefs.h" |
| #include "IceELFObjectWriter.h" |
| #include "IceGlobalInits.h" |
| #include "IceInstMIPS32.h" |
| #include "IceInstVarIter.h" |
| #include "IceLiveness.h" |
| #include "IceOperand.h" |
| #include "IcePhiLoweringImpl.h" |
| #include "IceRegistersMIPS32.h" |
| #include "IceTargetLoweringMIPS32.def" |
| #include "IceUtils.h" |
| #include "llvm/Support/MathExtras.h" |
| |
| namespace MIPS32 { |
| std::unique_ptr<::Ice::TargetLowering> createTargetLowering(::Ice::Cfg *Func) { |
| return ::Ice::MIPS32::TargetMIPS32::create(Func); |
| } |
| |
| std::unique_ptr<::Ice::TargetDataLowering> |
| createTargetDataLowering(::Ice::GlobalContext *Ctx) { |
| return ::Ice::MIPS32::TargetDataMIPS32::create(Ctx); |
| } |
| |
| std::unique_ptr<::Ice::TargetHeaderLowering> |
| createTargetHeaderLowering(::Ice::GlobalContext *Ctx) { |
| return ::Ice::MIPS32::TargetHeaderMIPS32::create(Ctx); |
| } |
| |
| void staticInit(::Ice::GlobalContext *Ctx) { |
| ::Ice::MIPS32::TargetMIPS32::staticInit(Ctx); |
| } |
| |
| bool shouldBePooled(const ::Ice::Constant *C) { |
| return ::Ice::MIPS32::TargetMIPS32::shouldBePooled(C); |
| } |
| |
| ::Ice::Type getPointerType() { |
| return ::Ice::MIPS32::TargetMIPS32::getPointerType(); |
| } |
| |
| } // end of namespace MIPS32 |
| |
| namespace Ice { |
| namespace MIPS32 { |
| |
| using llvm::isInt; |
| |
| namespace { |
| |
| // The maximum number of arguments to pass in GPR registers. |
| constexpr uint32_t MIPS32_MAX_GPR_ARG = 4; |
| |
| std::array<RegNumT, MIPS32_MAX_GPR_ARG> GPRArgInitializer; |
| std::array<RegNumT, MIPS32_MAX_GPR_ARG / 2> I64ArgInitializer; |
| |
| constexpr uint32_t MIPS32_MAX_FP_ARG = 2; |
| |
| std::array<RegNumT, MIPS32_MAX_FP_ARG> FP32ArgInitializer; |
| std::array<RegNumT, MIPS32_MAX_FP_ARG> FP64ArgInitializer; |
| |
| const char *getRegClassName(RegClass C) { |
| auto ClassNum = static_cast<RegClassMIPS32>(C); |
| assert(ClassNum < RCMIPS32_NUM); |
| switch (ClassNum) { |
| default: |
| assert(C < RC_Target); |
| return regClassString(C); |
| // Add handling of new register classes below. |
| } |
| } |
| |
| // Stack alignment |
| constexpr uint32_t MIPS32_STACK_ALIGNMENT_BYTES = 16; |
| |
| // Value is in bytes. Return Value adjusted to the next highest multiple of the |
| // stack alignment required for the given type. |
| uint32_t applyStackAlignmentTy(uint32_t Value, Type Ty) { |
| size_t typeAlignInBytes = typeWidthInBytes(Ty); |
| // Vectors are stored on stack with the same alignment as that of int type |
| if (isVectorType(Ty)) |
| typeAlignInBytes = typeWidthInBytes(IceType_i64); |
| return Utils::applyAlignment(Value, typeAlignInBytes); |
| } |
| |
| // Value is in bytes. Return Value adjusted to the next highest multiple of the |
| // stack alignment. |
| uint32_t applyStackAlignment(uint32_t Value) { |
| return Utils::applyAlignment(Value, MIPS32_STACK_ALIGNMENT_BYTES); |
| } |
| |
| } // end of anonymous namespace |
| |
| TargetMIPS32::TargetMIPS32(Cfg *Func) |
| : TargetLowering(Func), NeedSandboxing(SandboxingType == ST_NaCl) {} |
| |
| void TargetMIPS32::assignVarStackSlots(VarList &SortedSpilledVariables, |
| size_t SpillAreaPaddingBytes, |
| size_t SpillAreaSizeBytes, |
| size_t GlobalsAndSubsequentPaddingSize) { |
| const VariablesMetadata *VMetadata = Func->getVMetadata(); |
| size_t GlobalsSpaceUsed = SpillAreaPaddingBytes; |
| size_t NextStackOffset = SpillAreaPaddingBytes; |
| CfgVector<size_t> LocalsSize(Func->getNumNodes()); |
| const bool SimpleCoalescing = !callsReturnsTwice(); |
| for (Variable *Var : SortedSpilledVariables) { |
| size_t Increment = typeWidthInBytesOnStack(Var->getType()); |
| if (SimpleCoalescing && VMetadata->isTracked(Var)) { |
| if (VMetadata->isMultiBlock(Var)) { |
| GlobalsSpaceUsed += Increment; |
| NextStackOffset = GlobalsSpaceUsed; |
| } else { |
| SizeT NodeIndex = VMetadata->getLocalUseNode(Var)->getIndex(); |
| LocalsSize[NodeIndex] += Increment; |
| NextStackOffset = SpillAreaPaddingBytes + |
| GlobalsAndSubsequentPaddingSize + |
| LocalsSize[NodeIndex]; |
| } |
| } else { |
| NextStackOffset += Increment; |
| } |
| Var->setStackOffset(SpillAreaSizeBytes - NextStackOffset); |
| } |
| } |
| |
| void TargetMIPS32::staticInit(GlobalContext *Ctx) { |
| (void)Ctx; |
| RegNumT::setLimit(RegMIPS32::Reg_NUM); |
| SmallBitVector IntegerRegisters(RegMIPS32::Reg_NUM); |
| SmallBitVector I64PairRegisters(RegMIPS32::Reg_NUM); |
| SmallBitVector Float32Registers(RegMIPS32::Reg_NUM); |
| SmallBitVector Float64Registers(RegMIPS32::Reg_NUM); |
| SmallBitVector VectorRegisters(RegMIPS32::Reg_NUM); |
| SmallBitVector InvalidRegisters(RegMIPS32::Reg_NUM); |
| #define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \ |
| isI64Pair, isFP32, isFP64, isVec128, alias_init) \ |
| IntegerRegisters[RegMIPS32::val] = isInt; \ |
| I64PairRegisters[RegMIPS32::val] = isI64Pair; \ |
| Float32Registers[RegMIPS32::val] = isFP32; \ |
| Float64Registers[RegMIPS32::val] = isFP64; \ |
| VectorRegisters[RegMIPS32::val] = isVec128; \ |
| RegisterAliases[RegMIPS32::val].resize(RegMIPS32::Reg_NUM); \ |
| for (SizeT RegAlias : alias_init) { \ |
| assert(!RegisterAliases[RegMIPS32::val][RegAlias] && \ |
| "Duplicate alias for " #val); \ |
| RegisterAliases[RegMIPS32::val].set(RegAlias); \ |
| } \ |
| RegisterAliases[RegMIPS32::val].resize(RegMIPS32::Reg_NUM); \ |
| assert(RegisterAliases[RegMIPS32::val][RegMIPS32::val]); |
| REGMIPS32_TABLE; |
| #undef X |
| |
| // TODO(mohit.bhakkad): Change these inits once we provide argument related |
| // field in register tables |
| for (size_t i = 0; i < MIPS32_MAX_GPR_ARG; i++) |
| GPRArgInitializer[i] = RegNumT::fixme(RegMIPS32::Reg_A0 + i); |
| |
| for (size_t i = 0; i < MIPS32_MAX_GPR_ARG / 2; i++) |
| I64ArgInitializer[i] = RegNumT::fixme(RegMIPS32::Reg_A0A1 + i); |
| |
| for (size_t i = 0; i < MIPS32_MAX_FP_ARG; i++) { |
| FP32ArgInitializer[i] = RegNumT::fixme(RegMIPS32::Reg_F12 + i * 2); |
| FP64ArgInitializer[i] = RegNumT::fixme(RegMIPS32::Reg_F12F13 + i); |
| } |
| |
| TypeToRegisterSet[IceType_void] = InvalidRegisters; |
| TypeToRegisterSet[IceType_i1] = IntegerRegisters; |
| TypeToRegisterSet[IceType_i8] = IntegerRegisters; |
| TypeToRegisterSet[IceType_i16] = IntegerRegisters; |
| TypeToRegisterSet[IceType_i32] = IntegerRegisters; |
| TypeToRegisterSet[IceType_i64] = IntegerRegisters; |
| TypeToRegisterSet[IceType_f32] = Float32Registers; |
| TypeToRegisterSet[IceType_f64] = Float64Registers; |
| TypeToRegisterSet[IceType_v4i1] = VectorRegisters; |
| TypeToRegisterSet[IceType_v8i1] = VectorRegisters; |
| TypeToRegisterSet[IceType_v16i1] = VectorRegisters; |
| TypeToRegisterSet[IceType_v16i8] = VectorRegisters; |
| TypeToRegisterSet[IceType_v8i16] = VectorRegisters; |
| TypeToRegisterSet[IceType_v4i32] = VectorRegisters; |
| TypeToRegisterSet[IceType_v4f32] = VectorRegisters; |
| |
| for (size_t i = 0; i < llvm::array_lengthof(TypeToRegisterSet); ++i) |
| TypeToRegisterSetUnfiltered[i] = TypeToRegisterSet[i]; |
| |
| filterTypeToRegisterSet(Ctx, RegMIPS32::Reg_NUM, TypeToRegisterSet, |
| llvm::array_lengthof(TypeToRegisterSet), |
| RegMIPS32::getRegName, getRegClassName); |
| } |
| |
| void TargetMIPS32::unsetIfNonLeafFunc() { |
| for (CfgNode *Node : Func->getNodes()) { |
| for (Inst &Instr : Node->getInsts()) { |
| if (llvm::isa<InstCall>(&Instr)) { |
| // Unset MaybeLeafFunc if call instruction exists. |
| MaybeLeafFunc = false; |
| return; |
| } |
| } |
| } |
| } |
| |
| uint32_t TargetMIPS32::getStackAlignment() const { |
| return MIPS32_STACK_ALIGNMENT_BYTES; |
| } |
| |
| uint32_t TargetMIPS32::getCallStackArgumentsSizeBytes(const InstCall *Call) { |
| TargetMIPS32::CallingConv CC; |
| RegNumT DummyReg; |
| size_t OutArgsSizeBytes = 0; |
| Variable *Dest = Call->getDest(); |
| bool PartialOnStack = false; |
| if (Dest != nullptr && isVectorFloatingType(Dest->getType())) { |
| CC.discardReg(RegMIPS32::Reg_A0); |
| // Next vector is partially on stack |
| PartialOnStack = true; |
| } |
| for (SizeT i = 0, NumArgs = Call->getNumArgs(); i < NumArgs; ++i) { |
| Operand *Arg = legalizeUndef(Call->getArg(i)); |
| const Type Ty = Arg->getType(); |
| RegNumT RegNum; |
| if (CC.argInReg(Ty, i, &RegNum)) { |
| // If PartialOnStack is true and if this is a vector type then last two |
| // elements are on stack |
| if (PartialOnStack && isVectorType(Ty)) { |
| OutArgsSizeBytes = applyStackAlignmentTy(OutArgsSizeBytes, IceType_i64); |
| OutArgsSizeBytes += typeWidthInBytesOnStack(IceType_i32) * 2; |
| } |
| continue; |
| } |
| OutArgsSizeBytes = applyStackAlignmentTy(OutArgsSizeBytes, Ty); |
| OutArgsSizeBytes += typeWidthInBytesOnStack(Ty); |
| } |
| // Add size of argument save area |
| constexpr int BytesPerStackArg = 4; |
| OutArgsSizeBytes += MIPS32_MAX_GPR_ARG * BytesPerStackArg; |
| return applyStackAlignment(OutArgsSizeBytes); |
| } |
| |
| namespace { |
| inline uint64_t getConstantMemoryOrder(Operand *Opnd) { |
| if (auto *Integer = llvm::dyn_cast<ConstantInteger32>(Opnd)) |
| return Integer->getValue(); |
| return Intrinsics::MemoryOrderInvalid; |
| } |
| } |
| |
| void TargetMIPS32::genTargetHelperCallFor(Inst *Instr) { |
| constexpr bool NoTailCall = false; |
| constexpr bool IsTargetHelperCall = true; |
| Variable *Dest = Instr->getDest(); |
| const Type DestTy = Dest ? Dest->getType() : IceType_void; |
| |
| switch (Instr->getKind()) { |
| default: |
| return; |
| case Inst::Select: { |
| if (isVectorType(DestTy)) { |
| Operand *SrcT = llvm::cast<InstSelect>(Instr)->getTrueOperand(); |
| Operand *SrcF = llvm::cast<InstSelect>(Instr)->getFalseOperand(); |
| Operand *Cond = llvm::cast<InstSelect>(Instr)->getCondition(); |
| Variable *T = Func->makeVariable(DestTy); |
| auto *Undef = ConstantUndef::create(Ctx, DestTy); |
| Context.insert<InstAssign>(T, Undef); |
| auto *VarVecOn32 = llvm::cast<VariableVecOn32>(T); |
| VarVecOn32->initVecElement(Func); |
| for (SizeT I = 0; I < typeNumElements(DestTy); ++I) { |
| auto *Index = Ctx->getConstantInt32(I); |
| auto *OpC = Func->makeVariable(typeElementType(Cond->getType())); |
| Context.insert<InstExtractElement>(OpC, Cond, Index); |
| auto *OpT = Func->makeVariable(typeElementType(DestTy)); |
| Context.insert<InstExtractElement>(OpT, SrcT, Index); |
| auto *OpF = Func->makeVariable(typeElementType(DestTy)); |
| Context.insert<InstExtractElement>(OpF, SrcF, Index); |
| auto *Dst = Func->makeVariable(typeElementType(DestTy)); |
| Variable *DestT = Func->makeVariable(DestTy); |
| Context.insert<InstSelect>(Dst, OpC, OpT, OpF); |
| Context.insert<InstInsertElement>(DestT, T, Dst, Index); |
| T = DestT; |
| } |
| Context.insert<InstAssign>(Dest, T); |
| Instr->setDeleted(); |
| } |
| return; |
| } |
| case Inst::Fcmp: { |
| if (isVectorType(DestTy)) { |
| InstFcmp::FCond Cond = llvm::cast<InstFcmp>(Instr)->getCondition(); |
| Operand *Src0 = Instr->getSrc(0); |
| Operand *Src1 = Instr->getSrc(1); |
| Variable *T = Func->makeVariable(IceType_v4f32); |
| auto *Undef = ConstantUndef::create(Ctx, IceType_v4f32); |
| Context.insert<InstAssign>(T, Undef); |
| auto *VarVecOn32 = llvm::cast<VariableVecOn32>(T); |
| VarVecOn32->initVecElement(Func); |
| for (SizeT I = 0; I < typeNumElements(IceType_v4f32); ++I) { |
| auto *Index = Ctx->getConstantInt32(I); |
| auto *Op0 = Func->makeVariable(IceType_f32); |
| Context.insert<InstExtractElement>(Op0, Src0, Index); |
| auto *Op1 = Func->makeVariable(IceType_f32); |
| Context.insert<InstExtractElement>(Op1, Src1, Index); |
| auto *Dst = Func->makeVariable(IceType_f32); |
| Variable *DestT = Func->makeVariable(IceType_v4f32); |
| Context.insert<InstFcmp>(Cond, Dst, Op0, Op1); |
| Context.insert<InstInsertElement>(DestT, T, Dst, Index); |
| T = DestT; |
| } |
| Context.insert<InstAssign>(Dest, T); |
| Instr->setDeleted(); |
| } |
| return; |
| } |
| case Inst::Icmp: { |
| if (isVectorType(DestTy)) { |
| InstIcmp::ICond Cond = llvm::cast<InstIcmp>(Instr)->getCondition(); |
| Operand *Src0 = Instr->getSrc(0); |
| Operand *Src1 = Instr->getSrc(1); |
| const Type SrcType = Src0->getType(); |
| Variable *T = Func->makeVariable(DestTy); |
| auto *Undef = ConstantUndef::create(Ctx, DestTy); |
| Context.insert<InstAssign>(T, Undef); |
| auto *VarVecOn32 = llvm::cast<VariableVecOn32>(T); |
| VarVecOn32->initVecElement(Func); |
| for (SizeT I = 0; I < typeNumElements(SrcType); ++I) { |
| auto *Index = Ctx->getConstantInt32(I); |
| auto *Op0 = Func->makeVariable(typeElementType(SrcType)); |
| Context.insert<InstExtractElement>(Op0, Src0, Index); |
| auto *Op1 = Func->makeVariable(typeElementType(SrcType)); |
| Context.insert<InstExtractElement>(Op1, Src1, Index); |
| auto *Dst = Func->makeVariable(typeElementType(DestTy)); |
| Variable *DestT = Func->makeVariable(DestTy); |
| Context.insert<InstIcmp>(Cond, Dst, Op0, Op1); |
| Context.insert<InstInsertElement>(DestT, T, Dst, Index); |
| T = DestT; |
| } |
| Context.insert<InstAssign>(Dest, T); |
| Instr->setDeleted(); |
| } |
| return; |
| } |
| case Inst::Arithmetic: { |
| const InstArithmetic::OpKind Op = |
| llvm::cast<InstArithmetic>(Instr)->getOp(); |
| if (isVectorType(DestTy)) { |
| scalarizeArithmetic(Op, Dest, Instr->getSrc(0), Instr->getSrc(1)); |
| Instr->setDeleted(); |
| return; |
| } |
| switch (DestTy) { |
| default: |
| return; |
| case IceType_i64: { |
| RuntimeHelper HelperID = RuntimeHelper::H_Num; |
| switch (Op) { |
| default: |
| return; |
| case InstArithmetic::Udiv: |
| HelperID = RuntimeHelper::H_udiv_i64; |
| break; |
| case InstArithmetic::Sdiv: |
| HelperID = RuntimeHelper::H_sdiv_i64; |
| break; |
| case InstArithmetic::Urem: |
| HelperID = RuntimeHelper::H_urem_i64; |
| break; |
| case InstArithmetic::Srem: |
| HelperID = RuntimeHelper::H_srem_i64; |
| break; |
| } |
| |
| if (HelperID == RuntimeHelper::H_Num) { |
| return; |
| } |
| |
| Operand *TargetHelper = Ctx->getRuntimeHelperFunc(HelperID); |
| constexpr SizeT MaxArgs = 2; |
| auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(Instr->getSrc(0)); |
| Call->addArg(Instr->getSrc(1)); |
| Instr->setDeleted(); |
| return; |
| } |
| case IceType_f32: |
| case IceType_f64: { |
| if (Op != InstArithmetic::Frem) { |
| return; |
| } |
| constexpr SizeT MaxArgs = 2; |
| Operand *TargetHelper = Ctx->getRuntimeHelperFunc( |
| DestTy == IceType_f32 ? RuntimeHelper::H_frem_f32 |
| : RuntimeHelper::H_frem_f64); |
| auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(Instr->getSrc(0)); |
| Call->addArg(Instr->getSrc(1)); |
| Instr->setDeleted(); |
| return; |
| } |
| } |
| llvm::report_fatal_error("Control flow should never have reached here."); |
| } |
| case Inst::Cast: { |
| Operand *Src0 = Instr->getSrc(0); |
| const Type SrcTy = Src0->getType(); |
| auto *CastInstr = llvm::cast<InstCast>(Instr); |
| const InstCast::OpKind CastKind = CastInstr->getCastKind(); |
| |
| if (isVectorType(DestTy)) { |
| Variable *T = Func->makeVariable(DestTy); |
| auto *VarVecOn32 = llvm::cast<VariableVecOn32>(T); |
| VarVecOn32->initVecElement(Func); |
| auto *Undef = ConstantUndef::create(Ctx, DestTy); |
| Context.insert<InstAssign>(T, Undef); |
| for (SizeT I = 0; I < typeNumElements(DestTy); ++I) { |
| auto *Index = Ctx->getConstantInt32(I); |
| auto *Op = Func->makeVariable(typeElementType(SrcTy)); |
| Context.insert<InstExtractElement>(Op, Src0, Index); |
| auto *Dst = Func->makeVariable(typeElementType(DestTy)); |
| Variable *DestT = Func->makeVariable(DestTy); |
| Context.insert<InstCast>(CastKind, Dst, Op); |
| Context.insert<InstInsertElement>(DestT, T, Dst, Index); |
| T = DestT; |
| } |
| Context.insert<InstAssign>(Dest, T); |
| Instr->setDeleted(); |
| return; |
| } |
| |
| switch (CastKind) { |
| default: |
| return; |
| case InstCast::Fptosi: |
| case InstCast::Fptoui: { |
| if ((DestTy != IceType_i32) && (DestTy != IceType_i64)) { |
| return; |
| } |
| const bool DestIs32 = DestTy == IceType_i32; |
| const bool DestIsSigned = CastKind == InstCast::Fptosi; |
| const bool Src0IsF32 = isFloat32Asserting32Or64(SrcTy); |
| RuntimeHelper RTHFunc = RuntimeHelper::H_Num; |
| if (DestIsSigned) { |
| if (DestIs32) { |
| return; |
| } |
| RTHFunc = Src0IsF32 ? RuntimeHelper::H_fptosi_f32_i64 |
| : RuntimeHelper::H_fptosi_f64_i64; |
| } else { |
| RTHFunc = Src0IsF32 ? (DestIs32 ? RuntimeHelper::H_fptoui_f32_i32 |
| : RuntimeHelper::H_fptoui_f32_i64) |
| : (DestIs32 ? RuntimeHelper::H_fptoui_f64_i32 |
| : RuntimeHelper::H_fptoui_f64_i64); |
| } |
| Operand *TargetHelper = Ctx->getRuntimeHelperFunc(RTHFunc); |
| static constexpr SizeT MaxArgs = 1; |
| auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(Src0); |
| Instr->setDeleted(); |
| return; |
| } |
| case InstCast::Sitofp: |
| case InstCast::Uitofp: { |
| if ((SrcTy != IceType_i32) && (SrcTy != IceType_i64)) { |
| return; |
| } |
| const bool SourceIs32 = SrcTy == IceType_i32; |
| const bool SourceIsSigned = CastKind == InstCast::Sitofp; |
| const bool DestIsF32 = isFloat32Asserting32Or64(DestTy); |
| RuntimeHelper RTHFunc = RuntimeHelper::H_Num; |
| if (SourceIsSigned) { |
| if (SourceIs32) { |
| return; |
| } |
| RTHFunc = DestIsF32 ? RuntimeHelper::H_sitofp_i64_f32 |
| : RuntimeHelper::H_sitofp_i64_f64; |
| } else { |
| RTHFunc = DestIsF32 ? (SourceIs32 ? RuntimeHelper::H_uitofp_i32_f32 |
| : RuntimeHelper::H_uitofp_i64_f32) |
| : (SourceIs32 ? RuntimeHelper::H_uitofp_i32_f64 |
| : RuntimeHelper::H_uitofp_i64_f64); |
| } |
| Operand *TargetHelper = Ctx->getRuntimeHelperFunc(RTHFunc); |
| static constexpr SizeT MaxArgs = 1; |
| auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(Src0); |
| Instr->setDeleted(); |
| return; |
| } |
| case InstCast::Bitcast: { |
| if (DestTy == SrcTy) { |
| return; |
| } |
| Variable *CallDest = Dest; |
| RuntimeHelper HelperID = RuntimeHelper::H_Num; |
| switch (DestTy) { |
| default: |
| return; |
| case IceType_i8: |
| assert(SrcTy == IceType_v8i1); |
| HelperID = RuntimeHelper::H_bitcast_8xi1_i8; |
| CallDest = Func->makeVariable(IceType_i32); |
| break; |
| case IceType_i16: |
| assert(SrcTy == IceType_v16i1); |
| HelperID = RuntimeHelper::H_bitcast_16xi1_i16; |
| CallDest = Func->makeVariable(IceType_i32); |
| break; |
| case IceType_v8i1: { |
| assert(SrcTy == IceType_i8); |
| HelperID = RuntimeHelper::H_bitcast_i8_8xi1; |
| Variable *Src0AsI32 = Func->makeVariable(stackSlotType()); |
| // Arguments to functions are required to be at least 32 bits wide. |
| Context.insert<InstCast>(InstCast::Zext, Src0AsI32, Src0); |
| Src0 = Src0AsI32; |
| } break; |
| case IceType_v16i1: { |
| assert(SrcTy == IceType_i16); |
| HelperID = RuntimeHelper::H_bitcast_i16_16xi1; |
| Variable *Src0AsI32 = Func->makeVariable(stackSlotType()); |
| // Arguments to functions are required to be at least 32 bits wide. |
| Context.insert<InstCast>(InstCast::Zext, Src0AsI32, Src0); |
| Src0 = Src0AsI32; |
| } break; |
| } |
| constexpr SizeT MaxSrcs = 1; |
| InstCall *Call = makeHelperCall(HelperID, CallDest, MaxSrcs); |
| Call->addArg(Src0); |
| Context.insert(Call); |
| // The PNaCl ABI disallows i8/i16 return types, so truncate the helper |
| // call result to the appropriate type as necessary. |
| if (CallDest->getType() != DestTy) |
| Context.insert<InstCast>(InstCast::Trunc, Dest, CallDest); |
| Instr->setDeleted(); |
| return; |
| } |
| case InstCast::Trunc: { |
| if (DestTy == SrcTy) { |
| return; |
| } |
| if (!isVectorType(SrcTy)) { |
| return; |
| } |
| assert(typeNumElements(DestTy) == typeNumElements(SrcTy)); |
| assert(typeElementType(DestTy) == IceType_i1); |
| assert(isVectorIntegerType(SrcTy)); |
| return; |
| } |
| case InstCast::Sext: |
| case InstCast::Zext: { |
| if (DestTy == SrcTy) { |
| return; |
| } |
| if (!isVectorType(DestTy)) { |
| return; |
| } |
| assert(typeNumElements(DestTy) == typeNumElements(SrcTy)); |
| assert(typeElementType(SrcTy) == IceType_i1); |
| assert(isVectorIntegerType(DestTy)); |
| return; |
| } |
| } |
| llvm::report_fatal_error("Control flow should never have reached here."); |
| } |
| case Inst::IntrinsicCall: { |
| auto *IntrinsicCall = llvm::cast<InstIntrinsicCall>(Instr); |
| Intrinsics::IntrinsicID ID = IntrinsicCall->getIntrinsicInfo().ID; |
| if (isVectorType(DestTy) && ID == Intrinsics::Fabs) { |
| Operand *Src0 = IntrinsicCall->getArg(0); |
| GlobalString FabsFloat = Ctx->getGlobalString("llvm.fabs.f32"); |
| Operand *CallTarget = Ctx->getConstantExternSym(FabsFloat); |
| GlobalString FabsVec = Ctx->getGlobalString("llvm.fabs.v4f32"); |
| bool BadIntrinsic = false; |
| const Intrinsics::FullIntrinsicInfo *FullInfo = |
| Ctx->getIntrinsicsInfo().find(FabsVec, BadIntrinsic); |
| Intrinsics::IntrinsicInfo Info = FullInfo->Info; |
| |
| Variable *T = Func->makeVariable(IceType_v4f32); |
| auto *Undef = ConstantUndef::create(Ctx, IceType_v4f32); |
| Context.insert<InstAssign>(T, Undef); |
| auto *VarVecOn32 = llvm::cast<VariableVecOn32>(T); |
| VarVecOn32->initVecElement(Func); |
| |
| for (SizeT i = 0; i < typeNumElements(IceType_v4f32); ++i) { |
| auto *Index = Ctx->getConstantInt32(i); |
| auto *Op = Func->makeVariable(IceType_f32); |
| Context.insert<InstExtractElement>(Op, Src0, Index); |
| auto *Res = Func->makeVariable(IceType_f32); |
| Variable *DestT = Func->makeVariable(IceType_v4f32); |
| auto *Call = |
| Context.insert<InstIntrinsicCall>(1, Res, CallTarget, Info); |
| Call->addArg(Op); |
| Context.insert<InstInsertElement>(DestT, T, Res, Index); |
| T = DestT; |
| } |
| |
| Context.insert<InstAssign>(Dest, T); |
| |
| Instr->setDeleted(); |
| return; |
| } |
| switch (ID) { |
| default: |
| return; |
| case Intrinsics::AtomicLoad: { |
| if (DestTy != IceType_i64) |
| return; |
| if (!Intrinsics::isMemoryOrderValid( |
| ID, getConstantMemoryOrder(IntrinsicCall->getArg(1)))) { |
| Func->setError("Unexpected memory ordering for AtomicLoad"); |
| return; |
| } |
| Operand *Addr = IntrinsicCall->getArg(0); |
| Operand *TargetHelper = Ctx->getConstantExternSym( |
| Ctx->getGlobalString("__sync_val_compare_and_swap_8")); |
| static constexpr SizeT MaxArgs = 3; |
| auto *_0 = Ctx->getConstantZero(IceType_i64); |
| auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(Addr); |
| Call->addArg(_0); |
| Call->addArg(_0); |
| Context.insert<InstMIPS32Sync>(); |
| Instr->setDeleted(); |
| return; |
| } |
| case Intrinsics::AtomicStore: { |
| Operand *Val = IntrinsicCall->getArg(0); |
| if (Val->getType() != IceType_i64) |
| return; |
| if (!Intrinsics::isMemoryOrderValid( |
| ID, getConstantMemoryOrder(IntrinsicCall->getArg(2)))) { |
| Func->setError("Unexpected memory ordering for AtomicStore"); |
| return; |
| } |
| Operand *Addr = IntrinsicCall->getArg(1); |
| Variable *NoDest = nullptr; |
| Operand *TargetHelper = Ctx->getConstantExternSym( |
| Ctx->getGlobalString("__sync_lock_test_and_set_8")); |
| Context.insert<InstMIPS32Sync>(); |
| static constexpr SizeT MaxArgs = 2; |
| auto *Call = Context.insert<InstCall>(MaxArgs, NoDest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(Addr); |
| Call->addArg(Val); |
| Context.insert<InstMIPS32Sync>(); |
| Instr->setDeleted(); |
| return; |
| } |
| case Intrinsics::AtomicCmpxchg: { |
| if (DestTy != IceType_i64) |
| return; |
| if (!Intrinsics::isMemoryOrderValid( |
| ID, getConstantMemoryOrder(IntrinsicCall->getArg(3)), |
| getConstantMemoryOrder(IntrinsicCall->getArg(4)))) { |
| Func->setError("Unexpected memory ordering for AtomicCmpxchg"); |
| return; |
| } |
| Operand *Addr = IntrinsicCall->getArg(0); |
| Operand *Oldval = IntrinsicCall->getArg(1); |
| Operand *Newval = IntrinsicCall->getArg(2); |
| Operand *TargetHelper = Ctx->getConstantExternSym( |
| Ctx->getGlobalString("__sync_val_compare_and_swap_8")); |
| Context.insert<InstMIPS32Sync>(); |
| static constexpr SizeT MaxArgs = 3; |
| auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(Addr); |
| Call->addArg(Oldval); |
| Call->addArg(Newval); |
| Context.insert<InstMIPS32Sync>(); |
| Instr->setDeleted(); |
| return; |
| } |
| case Intrinsics::AtomicRMW: { |
| if (DestTy != IceType_i64) |
| return; |
| if (!Intrinsics::isMemoryOrderValid( |
| ID, getConstantMemoryOrder(IntrinsicCall->getArg(3)))) { |
| Func->setError("Unexpected memory ordering for AtomicRMW"); |
| return; |
| } |
| auto Operation = static_cast<Intrinsics::AtomicRMWOperation>( |
| llvm::cast<ConstantInteger32>(IntrinsicCall->getArg(0))->getValue()); |
| auto *Addr = IntrinsicCall->getArg(1); |
| auto *Newval = IntrinsicCall->getArg(2); |
| Operand *TargetHelper; |
| switch (Operation) { |
| case Intrinsics::AtomicAdd: |
| TargetHelper = Ctx->getConstantExternSym( |
| Ctx->getGlobalString("__sync_fetch_and_add_8")); |
| break; |
| case Intrinsics::AtomicSub: |
| TargetHelper = Ctx->getConstantExternSym( |
| Ctx->getGlobalString("__sync_fetch_and_sub_8")); |
| break; |
| case Intrinsics::AtomicOr: |
| TargetHelper = Ctx->getConstantExternSym( |
| Ctx->getGlobalString("__sync_fetch_and_or_8")); |
| break; |
| case Intrinsics::AtomicAnd: |
| TargetHelper = Ctx->getConstantExternSym( |
| Ctx->getGlobalString("__sync_fetch_and_and_8")); |
| break; |
| case Intrinsics::AtomicXor: |
| TargetHelper = Ctx->getConstantExternSym( |
| Ctx->getGlobalString("__sync_fetch_and_xor_8")); |
| break; |
| case Intrinsics::AtomicExchange: |
| TargetHelper = Ctx->getConstantExternSym( |
| Ctx->getGlobalString("__sync_lock_test_and_set_8")); |
| break; |
| default: |
| llvm::report_fatal_error("Unknown AtomicRMW operation"); |
| return; |
| } |
| Context.insert<InstMIPS32Sync>(); |
| static constexpr SizeT MaxArgs = 2; |
| auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(Addr); |
| Call->addArg(Newval); |
| Context.insert<InstMIPS32Sync>(); |
| Instr->setDeleted(); |
| return; |
| } |
| case Intrinsics::Ctpop: { |
| Operand *Src0 = IntrinsicCall->getArg(0); |
| Operand *TargetHelper = |
| Ctx->getRuntimeHelperFunc(isInt32Asserting32Or64(Src0->getType()) |
| ? RuntimeHelper::H_call_ctpop_i32 |
| : RuntimeHelper::H_call_ctpop_i64); |
| static constexpr SizeT MaxArgs = 1; |
| auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(Src0); |
| Instr->setDeleted(); |
| return; |
| } |
| case Intrinsics::Longjmp: { |
| static constexpr SizeT MaxArgs = 2; |
| static constexpr Variable *NoDest = nullptr; |
| Operand *TargetHelper = |
| Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_longjmp); |
| auto *Call = Context.insert<InstCall>(MaxArgs, NoDest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(IntrinsicCall->getArg(0)); |
| Call->addArg(IntrinsicCall->getArg(1)); |
| Instr->setDeleted(); |
| return; |
| } |
| case Intrinsics::Memcpy: { |
| static constexpr SizeT MaxArgs = 3; |
| static constexpr Variable *NoDest = nullptr; |
| Operand *TargetHelper = |
| Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_memcpy); |
| auto *Call = Context.insert<InstCall>(MaxArgs, NoDest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(IntrinsicCall->getArg(0)); |
| Call->addArg(IntrinsicCall->getArg(1)); |
| Call->addArg(IntrinsicCall->getArg(2)); |
| Instr->setDeleted(); |
| return; |
| } |
| case Intrinsics::Memmove: { |
| static constexpr SizeT MaxArgs = 3; |
| static constexpr Variable *NoDest = nullptr; |
| Operand *TargetHelper = |
| Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_memmove); |
| auto *Call = Context.insert<InstCall>(MaxArgs, NoDest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(IntrinsicCall->getArg(0)); |
| Call->addArg(IntrinsicCall->getArg(1)); |
| Call->addArg(IntrinsicCall->getArg(2)); |
| Instr->setDeleted(); |
| return; |
| } |
| case Intrinsics::Memset: { |
| Operand *ValOp = IntrinsicCall->getArg(1); |
| assert(ValOp->getType() == IceType_i8); |
| Variable *ValExt = Func->makeVariable(stackSlotType()); |
| Context.insert<InstCast>(InstCast::Zext, ValExt, ValOp); |
| |
| static constexpr SizeT MaxArgs = 3; |
| static constexpr Variable *NoDest = nullptr; |
| Operand *TargetHelper = |
| Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_memset); |
| auto *Call = Context.insert<InstCall>(MaxArgs, NoDest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(IntrinsicCall->getArg(0)); |
| Call->addArg(ValExt); |
| Call->addArg(IntrinsicCall->getArg(2)); |
| Instr->setDeleted(); |
| return; |
| } |
| case Intrinsics::NaClReadTP: { |
| if (SandboxingType == ST_NaCl) { |
| return; |
| } |
| static constexpr SizeT MaxArgs = 0; |
| assert(SandboxingType != ST_Nonsfi); |
| Operand *TargetHelper = |
| Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_read_tp); |
| Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, NoTailCall, |
| IsTargetHelperCall); |
| Instr->setDeleted(); |
| return; |
| } |
| case Intrinsics::Setjmp: { |
| static constexpr SizeT MaxArgs = 1; |
| Operand *TargetHelper = |
| Ctx->getRuntimeHelperFunc(RuntimeHelper::H_call_setjmp); |
| auto *Call = Context.insert<InstCall>(MaxArgs, Dest, TargetHelper, |
| NoTailCall, IsTargetHelperCall); |
| Call->addArg(IntrinsicCall->getArg(0)); |
| Instr->setDeleted(); |
| return; |
| } |
| } |
| llvm::report_fatal_error("Control flow should never have reached here."); |
| } |
| } |
| } |
| |
| void TargetMIPS32::findMaxStackOutArgsSize() { |
| // MinNeededOutArgsBytes should be updated if the Target ever creates a |
| // high-level InstCall that requires more stack bytes. |
| size_t MinNeededOutArgsBytes = 0; |
| if (!MaybeLeafFunc) |
| MinNeededOutArgsBytes = MIPS32_MAX_GPR_ARG * 4; |
| MaxOutArgsSizeBytes = MinNeededOutArgsBytes; |
| for (CfgNode *Node : Func->getNodes()) { |
| Context.init(Node); |
| while (!Context.atEnd()) { |
| PostIncrLoweringContext PostIncrement(Context); |
| Inst *CurInstr = iteratorToInst(Context.getCur()); |
| if (auto *Call = llvm::dyn_cast<InstCall>(CurInstr)) { |
| SizeT OutArgsSizeBytes = getCallStackArgumentsSizeBytes(Call); |
| MaxOutArgsSizeBytes = std::max(MaxOutArgsSizeBytes, OutArgsSizeBytes); |
| } |
| } |
| } |
| CurrentAllocaOffset = MaxOutArgsSizeBytes; |
| } |
| |
| void TargetMIPS32::translateO2() { |
| TimerMarker T(TimerStack::TT_O2, Func); |
| |
| // TODO(stichnot): share passes with X86? |
| // https://code.google.com/p/nativeclient/issues/detail?id=4094 |
| genTargetHelperCalls(); |
| |
| unsetIfNonLeafFunc(); |
| |
| findMaxStackOutArgsSize(); |
| |
| // Merge Alloca instructions, and lay out the stack. |
| static constexpr bool SortAndCombineAllocas = true; |
| Func->processAllocas(SortAndCombineAllocas); |
| Func->dump("After Alloca processing"); |
| |
| if (!getFlags().getEnablePhiEdgeSplit()) { |
| // Lower Phi instructions. |
| Func->placePhiLoads(); |
| if (Func->hasError()) |
| return; |
| Func->placePhiStores(); |
| if (Func->hasError()) |
| return; |
| Func->deletePhis(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After Phi lowering"); |
| } |
| |
| // Address mode optimization. |
| Func->getVMetadata()->init(VMK_SingleDefs); |
| Func->doAddressOpt(); |
| |
| // Argument lowering |
| Func->doArgLowering(); |
| |
| // Target lowering. This requires liveness analysis for some parts of the |
| // lowering decisions, such as compare/branch fusing. If non-lightweight |
| // liveness analysis is used, the instructions need to be renumbered first. |
| // TODO: This renumbering should only be necessary if we're actually |
| // calculating live intervals, which we only do for register allocation. |
| Func->renumberInstructions(); |
| if (Func->hasError()) |
| return; |
| |
| // TODO: It should be sufficient to use the fastest liveness calculation, |
| // i.e. livenessLightweight(). However, for some reason that slows down the |
| // rest of the translation. Investigate. |
| Func->liveness(Liveness_Basic); |
| if (Func->hasError()) |
| return; |
| Func->dump("After MIPS32 address mode opt"); |
| |
| Func->genCode(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After MIPS32 codegen"); |
| |
| // Register allocation. This requires instruction renumbering and full |
| // liveness analysis. |
| Func->renumberInstructions(); |
| if (Func->hasError()) |
| return; |
| Func->liveness(Liveness_Intervals); |
| if (Func->hasError()) |
| return; |
| // The post-codegen dump is done here, after liveness analysis and associated |
| // cleanup, to make the dump cleaner and more useful. |
| Func->dump("After initial MIPS32 codegen"); |
| // Validate the live range computations. The expensive validation call is |
| // deliberately only made when assertions are enabled. |
| assert(Func->validateLiveness()); |
| Func->getVMetadata()->init(VMK_All); |
| regAlloc(RAK_Global); |
| if (Func->hasError()) |
| return; |
| Func->dump("After linear scan regalloc"); |
| |
| if (getFlags().getEnablePhiEdgeSplit()) { |
| Func->advancedPhiLowering(); |
| Func->dump("After advanced Phi lowering"); |
| } |
| |
| // Stack frame mapping. |
| Func->genFrame(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After stack frame mapping"); |
| |
| postLowerLegalization(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After postLowerLegalization"); |
| |
| Func->contractEmptyNodes(); |
| Func->reorderNodes(); |
| |
| // Branch optimization. This needs to be done just before code emission. In |
| // particular, no transformations that insert or reorder CfgNodes should be |
| // done after branch optimization. We go ahead and do it before nop insertion |
| // to reduce the amount of work needed for searching for opportunities. |
| Func->doBranchOpt(); |
| Func->dump("After branch optimization"); |
| |
| // Nop insertion |
| if (getFlags().getShouldDoNopInsertion()) { |
| Func->doNopInsertion(); |
| } |
| } |
| |
| void TargetMIPS32::translateOm1() { |
| TimerMarker T(TimerStack::TT_Om1, Func); |
| |
| // TODO: share passes with X86? |
| genTargetHelperCalls(); |
| |
| unsetIfNonLeafFunc(); |
| |
| findMaxStackOutArgsSize(); |
| |
| // Do not merge Alloca instructions, and lay out the stack. |
| static constexpr bool SortAndCombineAllocas = false; |
| Func->processAllocas(SortAndCombineAllocas); |
| Func->dump("After Alloca processing"); |
| |
| Func->placePhiLoads(); |
| if (Func->hasError()) |
| return; |
| Func->placePhiStores(); |
| if (Func->hasError()) |
| return; |
| Func->deletePhis(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After Phi lowering"); |
| |
| Func->doArgLowering(); |
| |
| Func->genCode(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After initial MIPS32 codegen"); |
| |
| regAlloc(RAK_InfOnly); |
| if (Func->hasError()) |
| return; |
| Func->dump("After regalloc of infinite-weight variables"); |
| |
| Func->genFrame(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After stack frame mapping"); |
| |
| postLowerLegalization(); |
| if (Func->hasError()) |
| return; |
| Func->dump("After postLowerLegalization"); |
| |
| // Nop insertion |
| if (getFlags().getShouldDoNopInsertion()) { |
| Func->doNopInsertion(); |
| } |
| } |
| |
| bool TargetMIPS32::doBranchOpt(Inst *Instr, const CfgNode *NextNode) { |
| if (auto *Br = llvm::dyn_cast<InstMIPS32Br>(Instr)) { |
| return Br->optimizeBranch(NextNode); |
| } |
| return false; |
| } |
| |
| namespace { |
| |
| const char *RegNames[RegMIPS32::Reg_NUM] = { |
| #define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \ |
| isI64Pair, isFP32, isFP64, isVec128, alias_init) \ |
| name, |
| REGMIPS32_TABLE |
| #undef X |
| }; |
| |
| } // end of anonymous namespace |
| |
| const char *RegMIPS32::getRegName(RegNumT RegNum) { |
| RegNum.assertIsValid(); |
| return RegNames[RegNum]; |
| } |
| |
| const char *TargetMIPS32::getRegName(RegNumT RegNum, Type Ty) const { |
| (void)Ty; |
| return RegMIPS32::getRegName(RegNum); |
| } |
| |
| Variable *TargetMIPS32::getPhysicalRegister(RegNumT RegNum, Type Ty) { |
| if (Ty == IceType_void) |
| Ty = IceType_i32; |
| if (PhysicalRegisters[Ty].empty()) |
| PhysicalRegisters[Ty].resize(RegMIPS32::Reg_NUM); |
| RegNum.assertIsValid(); |
| Variable *Reg = PhysicalRegisters[Ty][RegNum]; |
| if (Reg == nullptr) { |
| Reg = Func->makeVariable(Ty); |
| Reg->setRegNum(RegNum); |
| PhysicalRegisters[Ty][RegNum] = Reg; |
| // Specially mark a named physical register as an "argument" so that it is |
| // considered live upon function entry. Otherwise it's possible to get |
| // liveness validation errors for saving callee-save registers. |
| Func->addImplicitArg(Reg); |
| // Don't bother tracking the live range of a named physical register. |
| Reg->setIgnoreLiveness(); |
| } |
| return Reg; |
| } |
| |
| void TargetMIPS32::emitJumpTable(const Cfg *Func, |
| const InstJumpTable *JumpTable) const { |
| (void)Func; |
| (void)JumpTable; |
| UnimplementedError(getFlags()); |
| } |
| |
| /// Provide a trivial wrapper to legalize() for this common usage. |
| Variable *TargetMIPS32::legalizeToReg(Operand *From, RegNumT RegNum) { |
| return llvm::cast<Variable>(legalize(From, Legal_Reg, RegNum)); |
| } |
| |
| /// Legalize undef values to concrete values. |
| Operand *TargetMIPS32::legalizeUndef(Operand *From, RegNumT RegNum) { |
| (void)RegNum; |
| Type Ty = From->getType(); |
| if (llvm::isa<ConstantUndef>(From)) { |
| // Lower undefs to zero. Another option is to lower undefs to an |
| // uninitialized register; however, using an uninitialized register |
| // results in less predictable code. |
| // |
| // If in the future the implementation is changed to lower undef |
| // values to uninitialized registers, a FakeDef will be needed: |
| // Context.insert(InstFakeDef::create(Func, Reg)); |
| // This is in order to ensure that the live range of Reg is not |
| // overestimated. If the constant being lowered is a 64 bit value, |
| // then the result should be split and the lo and hi components will |
| // need to go in uninitialized registers. |
| if (isVectorType(Ty)) { |
| Variable *Var = makeReg(Ty, RegNum); |
| auto *Reg = llvm::cast<VariableVecOn32>(Var); |
| Reg->initVecElement(Func); |
| auto *Zero = getZero(); |
| for (Variable *Var : Reg->getContainers()) { |
| _mov(Var, Zero); |
| } |
| return Reg; |
| } |
| return Ctx->getConstantZero(Ty); |
| } |
| return From; |
| } |
| |
| Variable *TargetMIPS32::makeReg(Type Type, RegNumT RegNum) { |
| // There aren't any 64-bit integer registers for Mips32. |
| assert(Type != IceType_i64); |
| Variable *Reg = Func->makeVariable(Type); |
| if (RegNum.hasValue()) |
| Reg->setRegNum(RegNum); |
| else |
| Reg->setMustHaveReg(); |
| return Reg; |
| } |
| |
| OperandMIPS32Mem *TargetMIPS32::formMemoryOperand(Operand *Operand, Type Ty) { |
| // It may be the case that address mode optimization already creates an |
| // OperandMIPS32Mem, so in that case it wouldn't need another level of |
| // transformation. |
| if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(Operand)) { |
| return llvm::cast<OperandMIPS32Mem>(legalize(Mem)); |
| } |
| |
| // If we didn't do address mode optimization, then we only have a base/offset |
| // to work with. MIPS always requires a base register, so just use that to |
| // hold the operand. |
| auto *Base = llvm::cast<Variable>( |
| legalize(Operand, Legal_Reg | Legal_Rematerializable)); |
| const int32_t Offset = Base->hasStackOffset() ? Base->getStackOffset() : 0; |
| return OperandMIPS32Mem::create( |
| Func, Ty, Base, |
| llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(Offset))); |
| } |
| |
| void TargetMIPS32::emitVariable(const Variable *Var) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Ctx->getStrEmit(); |
| const Type FrameSPTy = IceType_i32; |
| if (Var->hasReg()) { |
| Str << '$' << getRegName(Var->getRegNum(), Var->getType()); |
| return; |
| } |
| if (Var->mustHaveReg()) { |
| llvm::report_fatal_error("Infinite-weight Variable (" + Var->getName() + |
| ") has no register assigned - function " + |
| Func->getFunctionName()); |
| } |
| const int32_t Offset = Var->getStackOffset(); |
| Str << Offset; |
| Str << "($" << getRegName(getFrameOrStackReg(), FrameSPTy); |
| Str << ")"; |
| } |
| |
| TargetMIPS32::CallingConv::CallingConv() |
| : GPRegsUsed(RegMIPS32::Reg_NUM), |
| GPRArgs(GPRArgInitializer.rbegin(), GPRArgInitializer.rend()), |
| I64Args(I64ArgInitializer.rbegin(), I64ArgInitializer.rend()), |
| VFPRegsUsed(RegMIPS32::Reg_NUM), |
| FP32Args(FP32ArgInitializer.rbegin(), FP32ArgInitializer.rend()), |
| FP64Args(FP64ArgInitializer.rbegin(), FP64ArgInitializer.rend()) {} |
| |
| // In MIPS O32 abi FP argument registers can be used only if first argument is |
| // of type float/double. UseFPRegs flag is used to care of that. Also FP arg |
| // registers can be used only for first 2 arguments, so we require argument |
| // number to make register allocation decisions. |
| bool TargetMIPS32::CallingConv::argInReg(Type Ty, uint32_t ArgNo, |
| RegNumT *Reg) { |
| if (isScalarIntegerType(Ty) || isVectorType(Ty)) |
| return argInGPR(Ty, Reg); |
| if (isScalarFloatingType(Ty)) { |
| if (ArgNo == 0) { |
| UseFPRegs = true; |
| return argInVFP(Ty, Reg); |
| } |
| if (UseFPRegs && ArgNo == 1) { |
| UseFPRegs = false; |
| return argInVFP(Ty, Reg); |
| } |
| return argInGPR(Ty, Reg); |
| } |
| llvm::report_fatal_error("argInReg: Invalid type."); |
| return false; |
| } |
| |
| bool TargetMIPS32::CallingConv::argInGPR(Type Ty, RegNumT *Reg) { |
| CfgVector<RegNumT> *Source; |
| |
| switch (Ty) { |
| default: { |
| llvm::report_fatal_error("argInGPR: Invalid type."); |
| return false; |
| } break; |
| case IceType_v4i1: |
| case IceType_v8i1: |
| case IceType_v16i1: |
| case IceType_v16i8: |
| case IceType_v8i16: |
| case IceType_v4i32: |
| case IceType_v4f32: |
| case IceType_i32: |
| case IceType_f32: { |
| Source = &GPRArgs; |
| } break; |
| case IceType_i64: |
| case IceType_f64: { |
| Source = &I64Args; |
| } break; |
| } |
| |
| discardUnavailableGPRsAndTheirAliases(Source); |
| |
| // If $4 is used for any scalar type (or returining v4f32) then the next |
| // vector type if passed in $6:$7:stack:stack |
| if (isVectorType(Ty)) { |
| alignGPR(Source); |
| } |
| |
| if (Source->empty()) { |
| GPRegsUsed.set(); |
| return false; |
| } |
| |
| *Reg = Source->back(); |
| // Note that we don't Source->pop_back() here. This is intentional. Notice how |
| // we mark all of Reg's aliases as Used. So, for the next argument, |
| // Source->back() is marked as unavailable, and it is thus implicitly popped |
| // from the stack. |
| GPRegsUsed |= RegisterAliases[*Reg]; |
| |
| // All vector arguments irrespective of their base type are passed in GP |
| // registers. First vector argument is passed in $4:$5:$6:$7 and 2nd |
| // is passed in $6:$7:stack:stack. If it is 1st argument then discard |
| // $4:$5:$6:$7 otherwise discard $6:$7 only. |
| if (isVectorType(Ty)) { |
| if (((unsigned)*Reg) == RegMIPS32::Reg_A0) { |
| GPRegsUsed |= RegisterAliases[RegMIPS32::Reg_A1]; |
| GPRegsUsed |= RegisterAliases[RegMIPS32::Reg_A2]; |
| GPRegsUsed |= RegisterAliases[RegMIPS32::Reg_A3]; |
| } else { |
| GPRegsUsed |= RegisterAliases[RegMIPS32::Reg_A3]; |
| } |
| } |
| |
| return true; |
| } |
| |
| inline void TargetMIPS32::CallingConv::discardNextGPRAndItsAliases( |
| CfgVector<RegNumT> *Regs) { |
| GPRegsUsed |= RegisterAliases[Regs->back()]; |
| Regs->pop_back(); |
| } |
| |
| inline void TargetMIPS32::CallingConv::alignGPR(CfgVector<RegNumT> *Regs) { |
| if (Regs->back() == RegMIPS32::Reg_A1 || Regs->back() == RegMIPS32::Reg_A3) |
| discardNextGPRAndItsAliases(Regs); |
| } |
| |
| // GPR are not packed when passing parameters. Thus, a function foo(i32, i64, |
| // i32) will have the first argument in a0, the second in a2-a3, and the third |
| // on the stack. To model this behavior, whenever we pop a register from Regs, |
| // we remove all of its aliases from the pool of available GPRs. This has the |
| // effect of computing the "closure" on the GPR registers. |
| void TargetMIPS32::CallingConv::discardUnavailableGPRsAndTheirAliases( |
| CfgVector<RegNumT> *Regs) { |
| while (!Regs->empty() && GPRegsUsed[Regs->back()]) { |
| discardNextGPRAndItsAliases(Regs); |
| } |
| } |
| |
| bool TargetMIPS32::CallingConv::argInVFP(Type Ty, RegNumT *Reg) { |
| CfgVector<RegNumT> *Source; |
| |
| switch (Ty) { |
| default: { |
| llvm::report_fatal_error("argInVFP: Invalid type."); |
| return false; |
| } break; |
| case IceType_f32: { |
| Source = &FP32Args; |
| } break; |
| case IceType_f64: { |
| Source = &FP64Args; |
| } break; |
| } |
| |
| discardUnavailableVFPRegsAndTheirAliases(Source); |
| |
| if (Source->empty()) { |
| VFPRegsUsed.set(); |
| return false; |
| } |
| |
| *Reg = Source->back(); |
| VFPRegsUsed |= RegisterAliases[*Reg]; |
| |
| // In MIPS O32 abi if fun arguments are (f32, i32) then one can not use reg_a0 |
| // for second argument even though it's free. f32 arg goes in reg_f12, i32 arg |
| // goes in reg_a1. Similarly if arguments are (f64, i32) second argument goes |
| // in reg_a3 and a0, a1 are not used. |
| Source = &GPRArgs; |
| // Discard one GPR reg for f32(4 bytes), two for f64(4 + 4 bytes) |
| if (Ty == IceType_f64) { |
| // In MIPS o32 abi, when we use GPR argument pairs to store F64 values, pair |
| // must be aligned at even register. Similarly when we discard GPR registers |
| // when some arguments from starting 16 bytes goes in FPR, we must take care |
| // of alignment. For example if fun args are (f32, f64, f32), for first f32 |
| // we discard a0, now for f64 argument, which will go in F14F15, we must |
| // first align GPR vector to even register by discarding a1, then discard |
| // two GPRs a2 and a3. Now last f32 argument will go on stack. |
| alignGPR(Source); |
| discardNextGPRAndItsAliases(Source); |
| } |
| discardNextGPRAndItsAliases(Source); |
| return true; |
| } |
| |
| void TargetMIPS32::CallingConv::discardUnavailableVFPRegsAndTheirAliases( |
| CfgVector<RegNumT> *Regs) { |
| while (!Regs->empty() && VFPRegsUsed[Regs->back()]) { |
| Regs->pop_back(); |
| } |
| } |
| |
| void TargetMIPS32::lowerArguments() { |
| VarList &Args = Func->getArgs(); |
| TargetMIPS32::CallingConv CC; |
| |
| // For each register argument, replace Arg in the argument list with the home |
| // register. Then generate an instruction in the prolog to copy the home |
| // register to the assigned location of Arg. |
| Context.init(Func->getEntryNode()); |
| Context.setInsertPoint(Context.getCur()); |
| |
| // v4f32 is returned through stack. $4 is setup by the caller and passed as |
| // first argument implicitly. Callee then copies the return vector at $4. |
| Variable *ImplicitRetVec = nullptr; |
| if (isVectorFloatingType(Func->getReturnType())) { |
| ImplicitRetVec = Func->makeVariable(IceType_i32); |
| ImplicitRetVec->setName(Func, "ImplicitRet_v4f32"); |
| ImplicitRetVec->setIsArg(); |
| Args.insert(Args.begin(), ImplicitRetVec); |
| setImplicitRet(ImplicitRetVec); |
| } |
| |
| for (SizeT i = 0, E = Args.size(); i < E; ++i) { |
| Variable *Arg = Args[i]; |
| Type Ty = Arg->getType(); |
| RegNumT RegNum; |
| if (!CC.argInReg(Ty, i, &RegNum)) { |
| continue; |
| } |
| Variable *RegisterArg = Func->makeVariable(Ty); |
| if (BuildDefs::dump()) { |
| RegisterArg->setName(Func, "home_reg:" + Arg->getName()); |
| } |
| RegisterArg->setIsArg(); |
| Arg->setIsArg(false); |
| Args[i] = RegisterArg; |
| |
| if (isVectorType(Ty)) { |
| auto *RegisterArgVec = llvm::cast<VariableVecOn32>(RegisterArg); |
| RegisterArgVec->initVecElement(Func); |
| RegisterArgVec->getContainers()[0]->setRegNum( |
| RegNumT::fixme((unsigned)RegNum + 0)); |
| RegisterArgVec->getContainers()[1]->setRegNum( |
| RegNumT::fixme((unsigned)RegNum + 1)); |
| // First two elements of second vector argument are passed |
| // in $6:$7 and remaining two on stack. Do not assign register |
| // to this is second vector argument. |
| if (i == 0) { |
| RegisterArgVec->getContainers()[2]->setRegNum( |
| RegNumT::fixme((unsigned)RegNum + 2)); |
| RegisterArgVec->getContainers()[3]->setRegNum( |
| RegNumT::fixme((unsigned)RegNum + 3)); |
| } else { |
| RegisterArgVec->getContainers()[2]->setRegNum( |
| RegNumT::fixme(RegNumT())); |
| RegisterArgVec->getContainers()[3]->setRegNum( |
| RegNumT::fixme(RegNumT())); |
| } |
| } else { |
| switch (Ty) { |
| default: { RegisterArg->setRegNum(RegNum); } break; |
| case IceType_i64: { |
| auto *RegisterArg64 = llvm::cast<Variable64On32>(RegisterArg); |
| RegisterArg64->initHiLo(Func); |
| RegisterArg64->getLo()->setRegNum( |
| RegNumT::fixme(RegMIPS32::get64PairFirstRegNum(RegNum))); |
| RegisterArg64->getHi()->setRegNum( |
| RegNumT::fixme(RegMIPS32::get64PairSecondRegNum(RegNum))); |
| } break; |
| } |
| } |
| Context.insert<InstAssign>(Arg, RegisterArg); |
| } |
| |
| // Insert fake use of ImplicitRet_v4f32 to keep it live |
| if (ImplicitRetVec) { |
| for (CfgNode *Node : Func->getNodes()) { |
| for (Inst &Instr : Node->getInsts()) { |
| if (llvm::isa<InstRet>(&Instr)) { |
| Context.setInsertPoint(instToIterator(&Instr)); |
| Context.insert<InstFakeUse>(ImplicitRetVec); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| Type TargetMIPS32::stackSlotType() { return IceType_i32; } |
| |
| // Helper function for addProlog(). |
| // |
| // This assumes Arg is an argument passed on the stack. This sets the frame |
| // offset for Arg and updates InArgsSizeBytes according to Arg's width. For an |
| // I64 arg that has been split into Lo and Hi components, it calls itself |
| // recursively on the components, taking care to handle Lo first because of the |
| // little-endian architecture. Lastly, this function generates an instruction |
| // to copy Arg into its assigned register if applicable. |
| void TargetMIPS32::finishArgumentLowering(Variable *Arg, bool PartialOnStack, |
| Variable *FramePtr, |
| size_t BasicFrameOffset, |
| size_t *InArgsSizeBytes) { |
| const Type Ty = Arg->getType(); |
| *InArgsSizeBytes = applyStackAlignmentTy(*InArgsSizeBytes, Ty); |
| |
| // If $4 is used for any scalar type (or returining v4f32) then the next |
| // vector type if passed in $6:$7:stack:stack. Load 3nd and 4th element |
| // from agument stack. |
| if (auto *ArgVecOn32 = llvm::dyn_cast<VariableVecOn32>(Arg)) { |
| if (PartialOnStack == false) { |
| auto *Elem0 = ArgVecOn32->getContainers()[0]; |
| auto *Elem1 = ArgVecOn32->getContainers()[1]; |
| finishArgumentLowering(Elem0, PartialOnStack, FramePtr, BasicFrameOffset, |
| InArgsSizeBytes); |
| finishArgumentLowering(Elem1, PartialOnStack, FramePtr, BasicFrameOffset, |
| InArgsSizeBytes); |
| } |
| auto *Elem2 = ArgVecOn32->getContainers()[2]; |
| auto *Elem3 = ArgVecOn32->getContainers()[3]; |
| finishArgumentLowering(Elem2, PartialOnStack, FramePtr, BasicFrameOffset, |
| InArgsSizeBytes); |
| finishArgumentLowering(Elem3, PartialOnStack, FramePtr, BasicFrameOffset, |
| InArgsSizeBytes); |
| return; |
| } |
| |
| if (auto *Arg64On32 = llvm::dyn_cast<Variable64On32>(Arg)) { |
| Variable *const Lo = Arg64On32->getLo(); |
| Variable *const Hi = Arg64On32->getHi(); |
| finishArgumentLowering(Lo, PartialOnStack, FramePtr, BasicFrameOffset, |
| InArgsSizeBytes); |
| finishArgumentLowering(Hi, PartialOnStack, FramePtr, BasicFrameOffset, |
| InArgsSizeBytes); |
| return; |
| } |
| |
| assert(Ty != IceType_i64); |
| assert(!isVectorType(Ty)); |
| |
| const int32_t ArgStackOffset = BasicFrameOffset + *InArgsSizeBytes; |
| *InArgsSizeBytes += typeWidthInBytesOnStack(Ty); |
| |
| if (!Arg->hasReg()) { |
| Arg->setStackOffset(ArgStackOffset); |
| return; |
| } |
| |
| // If the argument variable has been assigned a register, we need to copy the |
| // value from the stack slot. |
| Variable *Parameter = Func->makeVariable(Ty); |
| Parameter->setMustNotHaveReg(); |
| Parameter->setStackOffset(ArgStackOffset); |
| _mov(Arg, Parameter); |
| } |
| |
| void TargetMIPS32::addProlog(CfgNode *Node) { |
| // Stack frame layout: |
| // |
| // +------------------------+ |
| // | 1. preserved registers | |
| // +------------------------+ |
| // | 2. padding | |
| // +------------------------+ |
| // | 3. global spill area | |
| // +------------------------+ |
| // | 4. padding | |
| // +------------------------+ |
| // | 5. local spill area | |
| // +------------------------+ |
| // | 6. padding | |
| // +------------------------+ |
| // | 7. allocas | |
| // +------------------------+ |
| // | 8. padding | |
| // +------------------------+ |
| // | 9. out args | |
| // +------------------------+ <--- StackPointer |
| // |
| // The following variables record the size in bytes of the given areas: |
| // * PreservedRegsSizeBytes: area 1 |
| // * SpillAreaPaddingBytes: area 2 |
| // * GlobalsSize: area 3 |
| // * GlobalsAndSubsequentPaddingSize: areas 3 - 4 |
| // * LocalsSpillAreaSize: area 5 |
| // * SpillAreaSizeBytes: areas 2 - 9 |
| // * maxOutArgsSizeBytes(): area 9 |
| |
| Context.init(Node); |
| Context.setInsertPoint(Context.getCur()); |
| |
| SmallBitVector CalleeSaves = getRegisterSet(RegSet_CalleeSave, RegSet_None); |
| RegsUsed = SmallBitVector(CalleeSaves.size()); |
| |
| VarList SortedSpilledVariables; |
| |
| size_t GlobalsSize = 0; |
| // If there is a separate locals area, this represents that area. Otherwise |
| // it counts any variable not counted by GlobalsSize. |
| SpillAreaSizeBytes = 0; |
| // If there is a separate locals area, this specifies the alignment for it. |
| uint32_t LocalsSlotsAlignmentBytes = 0; |
| // The entire spill locations area gets aligned to largest natural alignment |
| // of the variables that have a spill slot. |
| uint32_t SpillAreaAlignmentBytes = 0; |
| // For now, we don't have target-specific variables that need special |
| // treatment (no stack-slot-linked SpillVariable type). |
| std::function<bool(Variable *)> TargetVarHook = [](Variable *Var) { |
| static constexpr bool AssignStackSlot = false; |
| static constexpr bool DontAssignStackSlot = !AssignStackSlot; |
| if (llvm::isa<Variable64On32>(Var)) { |
| return DontAssignStackSlot; |
| } |
| return AssignStackSlot; |
| }; |
| |
| // Compute the list of spilled variables and bounds for GlobalsSize, etc. |
| getVarStackSlotParams(SortedSpilledVariables, RegsUsed, &GlobalsSize, |
| &SpillAreaSizeBytes, &SpillAreaAlignmentBytes, |
| &LocalsSlotsAlignmentBytes, TargetVarHook); |
| uint32_t LocalsSpillAreaSize = SpillAreaSizeBytes; |
| SpillAreaSizeBytes += GlobalsSize; |
| |
| PreservedGPRs.reserve(CalleeSaves.size()); |
| |
| // Consider FP and RA as callee-save / used as needed. |
| if (UsesFramePointer) { |
| if (RegsUsed[RegMIPS32::Reg_FP]) { |
| llvm::report_fatal_error("Frame pointer has been used."); |
| } |
| CalleeSaves[RegMIPS32::Reg_FP] = true; |
| RegsUsed[RegMIPS32::Reg_FP] = true; |
| } |
| if (!MaybeLeafFunc) { |
| CalleeSaves[RegMIPS32::Reg_RA] = true; |
| RegsUsed[RegMIPS32::Reg_RA] = true; |
| } |
| |
| // Make two passes over the used registers. The first pass records all the |
| // used registers -- and their aliases. Then, we figure out which GPR |
| // registers should be saved. |
| SmallBitVector ToPreserve(RegMIPS32::Reg_NUM); |
| for (SizeT i = 0; i < CalleeSaves.size(); ++i) { |
| if (CalleeSaves[i] && RegsUsed[i]) { |
| ToPreserve |= RegisterAliases[i]; |
| } |
| } |
| |
| uint32_t NumCallee = 0; |
| |
| // RegClasses is a tuple of |
| // |
| // <First Register in Class, Last Register in Class, Vector of Save Registers> |
| // |
| // We use this tuple to figure out which register we should save/restore |
| // during |
| // prolog/epilog. |
| using RegClassType = std::tuple<uint32_t, uint32_t, VarList *>; |
| const RegClassType RegClass = RegClassType( |
| RegMIPS32::Reg_GPR_First, RegMIPS32::Reg_FPR_Last, &PreservedGPRs); |
| const uint32_t FirstRegInClass = std::get<0>(RegClass); |
| const uint32_t LastRegInClass = std::get<1>(RegClass); |
| VarList *const PreservedRegsInClass = std::get<2>(RegClass); |
| for (uint32_t Reg = LastRegInClass; Reg > FirstRegInClass; Reg--) { |
| if (!ToPreserve[Reg]) { |
| continue; |
| } |
| ++NumCallee; |
| Variable *PhysicalRegister = getPhysicalRegister(RegNumT::fromInt(Reg)); |
| PreservedRegsSizeBytes += |
| typeWidthInBytesOnStack(PhysicalRegister->getType()); |
| PreservedRegsInClass->push_back(PhysicalRegister); |
| } |
| |
| Ctx->statsUpdateRegistersSaved(NumCallee); |
| |
| // Align the variables area. SpillAreaPaddingBytes is the size of the region |
| // after the preserved registers and before the spill areas. |
| // LocalsSlotsPaddingBytes is the amount of padding between the globals and |
| // locals area if they are separate. |
| assert(SpillAreaAlignmentBytes <= MIPS32_STACK_ALIGNMENT_BYTES); |
| (void)MIPS32_STACK_ALIGNMENT_BYTES; |
| assert(LocalsSlotsAlignmentBytes <= SpillAreaAlignmentBytes); |
| uint32_t SpillAreaPaddingBytes = 0; |
| uint32_t LocalsSlotsPaddingBytes = 0; |
| alignStackSpillAreas(PreservedRegsSizeBytes, SpillAreaAlignmentBytes, |
| GlobalsSize, LocalsSlotsAlignmentBytes, |
| &SpillAreaPaddingBytes, &LocalsSlotsPaddingBytes); |
| SpillAreaSizeBytes += SpillAreaPaddingBytes + LocalsSlotsPaddingBytes; |
| uint32_t GlobalsAndSubsequentPaddingSize = |
| GlobalsSize + LocalsSlotsPaddingBytes; |
| |
| // Adds the out args space to the stack, and align SP if necessary. |
| if (!NeedsStackAlignment) { |
| SpillAreaSizeBytes += MaxOutArgsSizeBytes * (VariableAllocaUsed ? 0 : 1); |
| } else { |
| SpillAreaSizeBytes = applyStackAlignment( |
| SpillAreaSizeBytes + |
| (VariableAllocaUsed ? VariableAllocaAlignBytes : MaxOutArgsSizeBytes)); |
| } |
| |
| // Combine fixed alloca with SpillAreaSize. |
| SpillAreaSizeBytes += FixedAllocaSizeBytes; |
| |
| TotalStackSizeBytes = |
| applyStackAlignment(PreservedRegsSizeBytes + SpillAreaSizeBytes); |
| |
| // Generate "addiu sp, sp, -TotalStackSizeBytes" |
| if (TotalStackSizeBytes) { |
| // Use the scratch register if needed to legalize the immediate. |
| Sandboxer(this).addiu_sp(-TotalStackSizeBytes); |
| } |
| |
| Ctx->statsUpdateFrameBytes(TotalStackSizeBytes); |
| |
| if (!PreservedGPRs.empty()) { |
| uint32_t StackOffset = TotalStackSizeBytes; |
| for (Variable *Var : *PreservedRegsInClass) { |
| Type RegType; |
| if (RegMIPS32::isFPRReg(Var->getRegNum())) |
| RegType = IceType_f32; |
| else |
| RegType = IceType_i32; |
| auto *PhysicalRegister = makeReg(RegType, Var->getRegNum()); |
| StackOffset -= typeWidthInBytesOnStack(RegType); |
| Variable *SP = getPhysicalRegister(RegMIPS32::Reg_SP); |
| OperandMIPS32Mem *MemoryLocation = OperandMIPS32Mem::create( |
| Func, RegType, SP, |
| llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(StackOffset))); |
| Sandboxer(this).sw(PhysicalRegister, MemoryLocation); |
| } |
| } |
| |
| Variable *FP = getPhysicalRegister(RegMIPS32::Reg_FP); |
| |
| // Generate "mov FP, SP" if needed. |
| if (UsesFramePointer) { |
| Variable *SP = getPhysicalRegister(RegMIPS32::Reg_SP); |
| _mov(FP, SP); |
| // Keep FP live for late-stage liveness analysis (e.g. asm-verbose mode). |
| Context.insert<InstFakeUse>(FP); |
| } |
| |
| // Fill in stack offsets for stack args, and copy args into registers for |
| // those that were register-allocated. Args are pushed right to left, so |
| // Arg[0] is closest to the stack/frame pointer. |
| const VarList &Args = Func->getArgs(); |
| size_t InArgsSizeBytes = MIPS32_MAX_GPR_ARG * 4; |
| TargetMIPS32::CallingConv CC; |
| uint32_t ArgNo = 0; |
| |
| for (Variable *Arg : Args) { |
| RegNumT DummyReg; |
| const Type Ty = Arg->getType(); |
| bool PartialOnStack; |
| // Skip arguments passed in registers. |
| if (CC.argInReg(Ty, ArgNo, &DummyReg)) { |
| // Load argument from stack: |
| // 1. If this is first vector argument and return type is v4f32. |
| // In this case $4 is used to pass stack address implicitly. |
| // 3rd and 4th element of vector argument is passed through stack. |
| // 2. If this is second vector argument. |
| if (ArgNo != 0 && isVectorType(Ty)) { |
| PartialOnStack = true; |
| finishArgumentLowering(Arg, PartialOnStack, FP, TotalStackSizeBytes, |
| &InArgsSizeBytes); |
| } |
| } else { |
| PartialOnStack = false; |
| finishArgumentLowering(Arg, PartialOnStack, FP, TotalStackSizeBytes, |
| &InArgsSizeBytes); |
| } |
| ++ArgNo; |
| } |
| |
| // Fill in stack offsets for locals. |
| assignVarStackSlots(SortedSpilledVariables, SpillAreaPaddingBytes, |
| SpillAreaSizeBytes, GlobalsAndSubsequentPaddingSize); |
| this->HasComputedFrame = true; |
| |
| if (BuildDefs::dump() && Func->isVerbose(IceV_Frame)) { |
| OstreamLocker _(Func->getContext()); |
| Ostream &Str = Func->getContext()->getStrDump(); |
| |
| Str << "Stack layout:\n"; |
| uint32_t SPAdjustmentPaddingSize = |
| SpillAreaSizeBytes - LocalsSpillAreaSize - |
| GlobalsAndSubsequentPaddingSize - SpillAreaPaddingBytes - |
| MaxOutArgsSizeBytes; |
| Str << " in-args = " << InArgsSizeBytes << " bytes\n" |
| << " preserved registers = " << PreservedRegsSizeBytes << " bytes\n" |
| << " spill area padding = " << SpillAreaPaddingBytes << " bytes\n" |
| << " globals spill area = " << GlobalsSize << " bytes\n" |
| << " globals-locals spill areas intermediate padding = " |
| << GlobalsAndSubsequentPaddingSize - GlobalsSize << " bytes\n" |
| << " locals spill area = " << LocalsSpillAreaSize << " bytes\n" |
| << " SP alignment padding = " << SPAdjustmentPaddingSize << " bytes\n"; |
| |
| Str << "Stack details:\n" |
| << " SP adjustment = " << SpillAreaSizeBytes << " bytes\n" |
| << " spill area alignment = " << SpillAreaAlignmentBytes << " bytes\n" |
| << " outgoing args size = " << MaxOutArgsSizeBytes << " bytes\n" |
| << " locals spill area alignment = " << LocalsSlotsAlignmentBytes |
| << " bytes\n" |
| << " is FP based = " << 1 << "\n"; |
| } |
| return; |
| } |
| |
| void TargetMIPS32::addEpilog(CfgNode *Node) { |
| InstList &Insts = Node->getInsts(); |
| InstList::reverse_iterator RI, E; |
| for (RI = Insts.rbegin(), E = Insts.rend(); RI != E; ++RI) { |
| if (llvm::isa<InstMIPS32Ret>(*RI)) |
| break; |
| } |
| if (RI == E) |
| return; |
| |
| // Convert the reverse_iterator position into its corresponding (forward) |
| // iterator position. |
| InstList::iterator InsertPoint = reverseToForwardIterator(RI); |
| --InsertPoint; |
| Context.init(Node); |
| Context.setInsertPoint(InsertPoint); |
| |
| Variable *SP = getPhysicalRegister(RegMIPS32::Reg_SP); |
| if (UsesFramePointer) { |
| Variable *FP = getPhysicalRegister(RegMIPS32::Reg_FP); |
| // For late-stage liveness analysis (e.g. asm-verbose mode), adding a fake |
| // use of SP before the assignment of SP=FP keeps previous SP adjustments |
| // from being dead-code eliminated. |
| Context.insert<InstFakeUse>(SP); |
| Sandboxer(this).reset_sp(FP); |
| } |
| |
| VarList::reverse_iterator RIter, END; |
| |
| if (!PreservedGPRs.empty()) { |
| uint32_t StackOffset = TotalStackSizeBytes - PreservedRegsSizeBytes; |
| for (RIter = PreservedGPRs.rbegin(), END = PreservedGPRs.rend(); |
| RIter != END; ++RIter) { |
| Type RegType; |
| if (RegMIPS32::isFPRReg((*RIter)->getRegNum())) |
| RegType = IceType_f32; |
| else |
| RegType = IceType_i32; |
| auto *PhysicalRegister = makeReg(RegType, (*RIter)->getRegNum()); |
| Variable *SP = getPhysicalRegister(RegMIPS32::Reg_SP); |
| OperandMIPS32Mem *MemoryLocation = OperandMIPS32Mem::create( |
| Func, RegType, SP, |
| llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(StackOffset))); |
| _lw(PhysicalRegister, MemoryLocation); |
| StackOffset += typeWidthInBytesOnStack(PhysicalRegister->getType()); |
| } |
| } |
| |
| if (TotalStackSizeBytes) { |
| Sandboxer(this).addiu_sp(TotalStackSizeBytes); |
| } |
| if (!getFlags().getUseSandboxing()) |
| return; |
| |
| Variable *RA = getPhysicalRegister(RegMIPS32::Reg_RA); |
| Variable *RetValue = nullptr; |
| if (RI->getSrcSize()) |
| RetValue = llvm::cast<Variable>(RI->getSrc(0)); |
| |
| Sandboxer(this).ret(RA, RetValue); |
| |
| RI->setDeleted(); |
| } |
| |
| Variable *TargetMIPS32::PostLoweringLegalizer::newBaseRegister( |
| Variable *Base, int32_t Offset, RegNumT ScratchRegNum) { |
| // Legalize will likely need a lui/ori combination, but if the top bits are |
| // all 0 from negating the offset and subtracting, we could use that instead. |
| const bool ShouldSub = Offset != 0 && (-Offset & 0xFFFF0000) == 0; |
| Variable *ScratchReg = Target->makeReg(IceType_i32, ScratchRegNum); |
| if (ShouldSub) { |
| Target->_addi(ScratchReg, Base, -Offset); |
| } else { |
| constexpr bool SignExt = true; |
| if (!OperandMIPS32Mem::canHoldOffset(Base->getType(), SignExt, Offset)) { |
| const uint32_t UpperBits = (Offset >> 16) & 0xFFFF; |
| const uint32_t LowerBits = Offset & 0xFFFF; |
| Target->_lui(ScratchReg, Target->Ctx->getConstantInt32(UpperBits)); |
| if (LowerBits) |
| Target->_ori(ScratchReg, ScratchReg, LowerBits); |
| Target->_addu(ScratchReg, ScratchReg, Base); |
| } else { |
| Target->_addiu(ScratchReg, Base, Offset); |
| } |
| } |
| |
| return ScratchReg; |
| } |
| |
| void TargetMIPS32::PostLoweringLegalizer::legalizeMovFp( |
| InstMIPS32MovFP64ToI64 *MovInstr) { |
| Variable *Dest = MovInstr->getDest(); |
| Operand *Src = MovInstr->getSrc(0); |
| const Type SrcTy = Src->getType(); |
| |
| if (Dest != nullptr && SrcTy == IceType_f64) { |
| int32_t Offset = Dest->getStackOffset(); |
| auto *Base = Target->getPhysicalRegister(Target->getFrameOrStackReg()); |
| OperandMIPS32Mem *TAddr = OperandMIPS32Mem::create( |
| Target->Func, IceType_f32, Base, |
| llvm::cast<ConstantInteger32>(Target->Ctx->getConstantInt32(Offset))); |
| OperandMIPS32Mem *Addr = legalizeMemOperand(TAddr); |
| auto *SrcV = llvm::cast<Variable>(Src); |
| Variable *SrcR; |
| if (MovInstr->getInt64Part() == Int64_Lo) { |
| SrcR = Target->makeReg( |
| IceType_f32, RegMIPS32::get64PairFirstRegNum(SrcV->getRegNum())); |
| } else { |
| SrcR = Target->makeReg( |
| IceType_f32, RegMIPS32::get64PairSecondRegNum(SrcV->getRegNum())); |
| } |
| Sandboxer(Target).sw(SrcR, Addr); |
| if (MovInstr->isDestRedefined()) { |
| Target->_set_dest_redefined(); |
| } |
| MovInstr->setDeleted(); |
| return; |
| } |
| |
| llvm::report_fatal_error("legalizeMovFp: Invalid operands"); |
| } |
| |
| void TargetMIPS32::PostLoweringLegalizer::legalizeMov(InstMIPS32Mov *MovInstr) { |
| Variable *Dest = MovInstr->getDest(); |
| assert(Dest != nullptr); |
| const Type DestTy = Dest->getType(); |
| assert(DestTy != IceType_i64); |
| |
| Operand *Src = MovInstr->getSrc(0); |
| const Type SrcTy = Src->getType(); |
| (void)SrcTy; |
| assert(SrcTy != IceType_i64); |
| |
| bool Legalized = false; |
| auto *SrcR = llvm::cast<Variable>(Src); |
| if (Dest->hasReg() && SrcR->hasReg()) { |
| // This might be a GP to/from FP move generated due to argument passing. |
| // Use mtc1/mfc1 instead of mov.[s/d] if src and dst registers are of |
| // different types. |
| const bool IsDstGPR = RegMIPS32::isGPRReg(Dest->getRegNum()); |
| const bool IsSrcGPR = RegMIPS32::isGPRReg(SrcR->getRegNum()); |
| const RegNumT SRegNum = SrcR->getRegNum(); |
| const RegNumT DRegNum = Dest->getRegNum(); |
| if (IsDstGPR != IsSrcGPR) { |
| if (IsDstGPR) { |
| // Dest is GPR and SrcR is FPR. Use mfc1. |
| int32_t TypeWidth = typeWidthInBytes(DestTy); |
| if (MovInstr->getDestHi() != nullptr) |
| TypeWidth += typeWidthInBytes(MovInstr->getDestHi()->getType()); |
| if (TypeWidth == 8) { |
| // Split it into two mfc1 instructions |
| Variable *SrcGPRHi = Target->makeReg( |
| IceType_f32, RegMIPS32::get64PairFirstRegNum(SRegNum)); |
| Variable *SrcGPRLo = Target->makeReg( |
| IceType_f32, RegMIPS32::get64PairSecondRegNum(SRegNum)); |
| Variable *DstFPRHi, *DstFPRLo; |
| if (MovInstr->getDestHi() != nullptr && Dest != nullptr) { |
| DstFPRHi = Target->makeReg(IceType_i32, |
| MovInstr->getDestHi()->getRegNum()); |
| DstFPRLo = Target->makeReg(IceType_i32, Dest->getRegNum()); |
| } else { |
| DstFPRHi = Target->makeReg( |
| IceType_i32, RegMIPS32::get64PairFirstRegNum(DRegNum)); |
| DstFPRLo = Target->makeReg( |
| IceType_i32, RegMIPS32::get64PairSecondRegNum(DRegNum)); |
| } |
| Target->_mov(DstFPRHi, SrcGPRHi); |
| Target->_mov(DstFPRLo, SrcGPRLo); |
| Legalized = true; |
| } else { |
| Variable *SrcGPR = Target->makeReg(IceType_f32, SRegNum); |
| Variable *DstFPR = Target->makeReg(IceType_i32, DRegNum); |
| Target->_mov(DstFPR, SrcGPR); |
| Legalized = true; |
| } |
| } else { |
| // Dest is FPR and SrcR is GPR. Use mtc1. |
| if (typeWidthInBytes(Dest->getType()) == 8) { |
| Variable *SrcGPRHi, *SrcGPRLo; |
| // SrcR could be $zero which is i32 |
| if (SRegNum == RegMIPS32::Reg_ZERO) { |
| SrcGPRHi = Target->makeReg(IceType_i32, SRegNum); |
| SrcGPRLo = SrcGPRHi; |
| } else { |
| // Split it into two mtc1 instructions |
| if (MovInstr->getSrcSize() == 2) { |
| const auto FirstReg = |
| (llvm::cast<Variable>(MovInstr->getSrc(0)))->getRegNum(); |
| const auto SecondReg = |
| (llvm::cast<Variable>(MovInstr->getSrc(1)))->getRegNum(); |
| SrcGPRHi = Target->makeReg(IceType_i32, FirstReg); |
| SrcGPRLo = Target->makeReg(IceType_i32, SecondReg); |
| } else { |
| SrcGPRLo = Target->makeReg( |
| IceType_i32, RegMIPS32::get64PairFirstRegNum(SRegNum)); |
| SrcGPRHi = Target->makeReg( |
| IceType_i32, RegMIPS32::get64PairSecondRegNum(SRegNum)); |
| } |
| } |
| Variable *DstFPRHi = Target->makeReg( |
| IceType_f32, RegMIPS32::get64PairFirstRegNum(DRegNum)); |
| Variable *DstFPRLo = Target->makeReg( |
| IceType_f32, RegMIPS32::get64PairSecondRegNum(DRegNum)); |
| Target->_mov(DstFPRHi, SrcGPRLo); |
| Target->_mov(DstFPRLo, SrcGPRHi); |
| Legalized = true; |
| } else { |
| Variable *SrcGPR = Target->makeReg(IceType_i32, SRegNum); |
| Variable *DstFPR = Target->makeReg(IceType_f32, DRegNum); |
| Target->_mov(DstFPR, SrcGPR); |
| Legalized = true; |
| } |
| } |
| } |
| if (Legalized) { |
| if (MovInstr->isDestRedefined()) { |
| Target->_set_dest_redefined(); |
| } |
| MovInstr->setDeleted(); |
| return; |
| } |
| } |
| |
| if (!Dest->hasReg()) { |
| auto *SrcR = llvm::cast<Variable>(Src); |
| assert(SrcR->hasReg()); |
| assert(!SrcR->isRematerializable()); |
| int32_t Offset = Dest->getStackOffset(); |
| |
| // This is a _mov(Mem(), Variable), i.e., a store. |
| auto *Base = Target->getPhysicalRegister(Target->getFrameOrStackReg()); |
| |
| OperandMIPS32Mem *TAddr = OperandMIPS32Mem::create( |
| Target->Func, DestTy, Base, |
| llvm::cast<ConstantInteger32>(Target->Ctx->getConstantInt32(Offset))); |
| OperandMIPS32Mem *TAddrHi = OperandMIPS32Mem::create( |
| Target->Func, DestTy, Base, |
| llvm::cast<ConstantInteger32>( |
| Target->Ctx->getConstantInt32(Offset + 4))); |
| OperandMIPS32Mem *Addr = legalizeMemOperand(TAddr); |
| |
| // FP arguments are passed in GP reg if first argument is in GP. In this |
| // case type of the SrcR is still FP thus we need to explicitly generate sw |
| // instead of swc1. |
| const RegNumT RegNum = SrcR->getRegNum(); |
| const bool IsSrcGPReg = RegMIPS32::isGPRReg(SrcR->getRegNum()); |
| if (SrcTy == IceType_f32 && IsSrcGPReg) { |
| Variable *SrcGPR = Target->makeReg(IceType_i32, RegNum); |
| Sandboxer(Target).sw(SrcGPR, Addr); |
| } else if (SrcTy == IceType_f64 && IsSrcGPReg) { |
| Variable *SrcGPRHi = |
| Target->makeReg(IceType_i32, RegMIPS32::get64PairFirstRegNum(RegNum)); |
| Variable *SrcGPRLo = Target->makeReg( |
| IceType_i32, RegMIPS32::get64PairSecondRegNum(RegNum)); |
| Sandboxer(Target).sw(SrcGPRHi, Addr); |
| OperandMIPS32Mem *AddrHi = legalizeMemOperand(TAddrHi); |
| Sandboxer(Target).sw(SrcGPRLo, AddrHi); |
| } else if (DestTy == IceType_f64 && IsSrcGPReg) { |
| const auto FirstReg = |
| (llvm::cast<Variable>(MovInstr->getSrc(0)))->getRegNum(); |
| const auto SecondReg = |
| (llvm::cast<Variable>(MovInstr->getSrc(1)))->getRegNum(); |
| Variable *SrcGPRHi = Target->makeReg(IceType_i32, FirstReg); |
| Variable *SrcGPRLo = Target->makeReg(IceType_i32, SecondReg); |
| Sandboxer(Target).sw(SrcGPRLo, Addr); |
| OperandMIPS32Mem *AddrHi = legalizeMemOperand(TAddrHi); |
| Sandboxer(Target).sw(SrcGPRHi, AddrHi); |
| } else { |
| Sandboxer(Target).sw(SrcR, Addr); |
| } |
| |
| Target->Context.insert<InstFakeDef>(Dest); |
| Legalized = true; |
| } else if (auto *Var = llvm::dyn_cast<Variable>(Src)) { |
| if (Var->isRematerializable()) { |
| // This is equivalent to an x86 _lea(RematOffset(%esp/%ebp), Variable). |
| |
| // ExtraOffset is only needed for stack-pointer based frames as we have |
| // to account for spill storage. |
| const int32_t ExtraOffset = |
| (Var->getRegNum() == Target->getFrameOrStackReg()) |
| ? Target->getFrameFixedAllocaOffset() |
| : 0; |
| |
| const int32_t Offset = Var->getStackOffset() + ExtraOffset; |
| Variable *Base = Target->getPhysicalRegister(Var->getRegNum()); |
| Variable *T = newBaseRegister(Base, Offset, Dest->getRegNum()); |
| Target->_mov(Dest, T); |
| Legalized = true; |
| } else { |
| if (!Var->hasReg()) { |
| // This is a _mov(Variable, Mem()), i.e., a load. |
| const int32_t Offset = Var->getStackOffset(); |
| auto *Base = Target->getPhysicalRegister(Target->getFrameOrStackReg()); |
| const RegNumT RegNum = Dest->getRegNum(); |
| const bool IsDstGPReg = RegMIPS32::isGPRReg(Dest->getRegNum()); |
| // If we are moving i64 to a double using stack then the address may |
| // not be aligned to 8-byte boundary as we split i64 into Hi-Lo parts |
| // and store them individually with 4-byte alignment. Load the Hi-Lo |
| // parts in TmpReg and move them to the dest using mtc1. |
| if (DestTy == IceType_f64 && !Utils::IsAligned(Offset, 8) && |
| !IsDstGPReg) { |
| auto *Reg = Target->makeReg(IceType_i32, Target->getReservedTmpReg()); |
| const RegNumT RegNum = Dest->getRegNum(); |
| Variable *DestLo = Target->makeReg( |
| IceType_f32, RegMIPS32::get64PairFirstRegNum(RegNum)); |
| Variable *DestHi = Target->makeReg( |
| IceType_f32, RegMIPS32::get64PairSecondRegNum(RegNum)); |
| OperandMIPS32Mem *AddrLo = OperandMIPS32Mem::create( |
| Target->Func, IceType_i32, Base, |
| llvm::cast<ConstantInteger32>( |
| Target->Ctx->getConstantInt32(Offset))); |
| OperandMIPS32Mem *AddrHi = OperandMIPS32Mem::create( |
| Target->Func, IceType_i32, Base, |
| llvm::cast<ConstantInteger32>( |
| Target->Ctx->getConstantInt32(Offset + 4))); |
| Sandboxer(Target).lw(Reg, AddrLo); |
| Target->_mov(DestLo, Reg); |
| Sandboxer(Target).lw(Reg, AddrHi); |
| Target->_mov(DestHi, Reg); |
| } else { |
| OperandMIPS32Mem *TAddr = OperandMIPS32Mem::create( |
| Target->Func, DestTy, Base, |
| llvm::cast<ConstantInteger32>( |
| Target->Ctx->getConstantInt32(Offset))); |
| OperandMIPS32Mem *Addr = legalizeMemOperand(TAddr); |
| OperandMIPS32Mem *TAddrHi = OperandMIPS32Mem::create( |
| Target->Func, DestTy, Base, |
| llvm::cast<ConstantInteger32>( |
| Target->Ctx->getConstantInt32(Offset + 4))); |
| // FP arguments are passed in GP reg if first argument is in GP. |
| // In this case type of the Dest is still FP thus we need to |
| // explicitly generate lw instead of lwc1. |
| if (DestTy == IceType_f32 && IsDstGPReg) { |
| Variable *DstGPR = Target->makeReg(IceType_i32, RegNum); |
| Sandboxer(Target).lw(DstGPR, Addr); |
| } else if (DestTy == IceType_f64 && IsDstGPReg) { |
| Variable *DstGPRHi = Target->makeReg( |
| IceType_i32, RegMIPS32::get64PairFirstRegNum(RegNum)); |
| Variable *DstGPRLo = Target->makeReg( |
| IceType_i32, RegMIPS32::get64PairSecondRegNum(RegNum)); |
| Sandboxer(Target).lw(DstGPRHi, Addr); |
| OperandMIPS32Mem *AddrHi = legalizeMemOperand(TAddrHi); |
| Sandboxer(Target).lw(DstGPRLo, AddrHi); |
| } else if (DestTy == IceType_f64 && IsDstGPReg) { |
| const auto FirstReg = |
| (llvm::cast<Variable>(MovInstr->getSrc(0)))->getRegNum(); |
| const auto SecondReg = |
| (llvm::cast<Variable>(MovInstr->getSrc(1)))->getRegNum(); |
| Variable *DstGPRHi = Target->makeReg(IceType_i32, FirstReg); |
| Variable *DstGPRLo = Target->makeReg(IceType_i32, SecondReg); |
| Sandboxer(Target).lw(DstGPRLo, Addr); |
| OperandMIPS32Mem *AddrHi = legalizeMemOperand(TAddrHi); |
| Sandboxer(Target).lw(DstGPRHi, AddrHi); |
| } else { |
| Sandboxer(Target).lw(Dest, Addr); |
| } |
| } |
| Legalized = true; |
| } |
| } |
| } |
| |
| if (Legalized) { |
| if (MovInstr->isDestRedefined()) { |
| Target->_set_dest_redefined(); |
| } |
| MovInstr->setDeleted(); |
| } |
| } |
| |
| OperandMIPS32Mem * |
| TargetMIPS32::PostLoweringLegalizer::legalizeMemOperand(OperandMIPS32Mem *Mem) { |
| if (llvm::isa<ConstantRelocatable>(Mem->getOffset())) { |
| return nullptr; |
| } |
| Variable *Base = Mem->getBase(); |
| auto *Ci32 = llvm::cast<ConstantInteger32>(Mem->getOffset()); |
| int32_t Offset = Ci32->getValue(); |
| |
| if (Base->isRematerializable()) { |
| const int32_t ExtraOffset = |
| (Base->getRegNum() == Target->getFrameOrStackReg()) |
| ? Target->getFrameFixedAllocaOffset() |
| : 0; |
| Offset += Base->getStackOffset() + ExtraOffset; |
| Base = Target->getPhysicalRegister(Base->getRegNum()); |
| } |
| |
| constexpr bool SignExt = true; |
| if (!OperandMIPS32Mem::canHoldOffset(Mem->getType(), SignExt, Offset)) { |
| Base = newBaseRegister(Base, Offset, Target->getReservedTmpReg()); |
| Offset = 0; |
| } |
| |
| return OperandMIPS32Mem::create( |
| Target->Func, Mem->getType(), Base, |
| llvm::cast<ConstantInteger32>(Target->Ctx->getConstantInt32(Offset))); |
| } |
| |
| Variable *TargetMIPS32::PostLoweringLegalizer::legalizeImmediate(int32_t Imm) { |
| Variable *Reg = nullptr; |
| if (!((std::numeric_limits<int16_t>::min() <= Imm) && |
| (Imm <= std::numeric_limits<int16_t>::max()))) { |
| const uint32_t UpperBits = (Imm >> 16) & 0xFFFF; |
| const uint32_t LowerBits = Imm & 0xFFFF; |
| Variable *TReg = Target->makeReg(IceType_i32, Target->getReservedTmpReg()); |
| Reg = Target->makeReg(IceType_i32, Target->getReservedTmpReg()); |
| if (LowerBits) { |
| Target->_lui(TReg, Target->Ctx->getConstantInt32(UpperBits)); |
| Target->_ori(Reg, TReg, LowerBits); |
| } else { |
| Target->_lui(Reg, Target->Ctx->getConstantInt32(UpperBits)); |
| } |
| } |
| return Reg; |
| } |
| |
| void TargetMIPS32::postLowerLegalization() { |
| Func->dump("Before postLowerLegalization"); |
| assert(hasComputedFrame()); |
| for (CfgNode *Node : Func->getNodes()) { |
| Context.init(Node); |
| PostLoweringLegalizer Legalizer(this); |
| while (!Context.atEnd()) { |
| PostIncrLoweringContext PostIncrement(Context); |
| Inst *CurInstr = iteratorToInst(Context.getCur()); |
| const SizeT NumSrcs = CurInstr->getSrcSize(); |
| Operand *Src0 = NumSrcs < 1 ? nullptr : CurInstr->getSrc(0); |
| Operand *Src1 = NumSrcs < 2 ? nullptr : CurInstr->getSrc(1); |
| auto *Src0V = llvm::dyn_cast_or_null<Variable>(Src0); |
| auto *Src0M = llvm::dyn_cast_or_null<OperandMIPS32Mem>(Src0); |
| auto *Src1M = llvm::dyn_cast_or_null<OperandMIPS32Mem>(Src1); |
| Variable *Dst = CurInstr->getDest(); |
| if (auto *MovInstr = llvm::dyn_cast<InstMIPS32Mov>(CurInstr)) { |
| Legalizer.legalizeMov(MovInstr); |
| continue; |
| } |
| if (auto *MovInstr = llvm::dyn_cast<InstMIPS32MovFP64ToI64>(CurInstr)) { |
| Legalizer.legalizeMovFp(MovInstr); |
| continue; |
| } |
| if (llvm::isa<InstMIPS32Sw>(CurInstr)) { |
| if (auto *LegalMem = Legalizer.legalizeMemOperand(Src1M)) { |
| Sandboxer(this).sw(Src0V, LegalMem); |
| CurInstr->setDeleted(); |
| } |
| continue; |
| } |
| if (llvm::isa<InstMIPS32Swc1>(CurInstr)) { |
| if (auto *LegalMem = Legalizer.legalizeMemOperand(Src1M)) { |
| _swc1(Src0V, LegalMem); |
| CurInstr->setDeleted(); |
| } |
| continue; |
| } |
| if (llvm::isa<InstMIPS32Sdc1>(CurInstr)) { |
| if (auto *LegalMem = Legalizer.legalizeMemOperand(Src1M)) { |
| _sdc1(Src0V, LegalMem); |
| CurInstr->setDeleted(); |
| } |
| continue; |
| } |
| if (llvm::isa<InstMIPS32Lw>(CurInstr)) { |
| if (auto *LegalMem = Legalizer.legalizeMemOperand(Src0M)) { |
| Sandboxer(this).lw(Dst, LegalMem); |
| CurInstr->setDeleted(); |
| } |
| continue; |
| } |
| if (llvm::isa<InstMIPS32Lwc1>(CurInstr)) { |
| if (auto *LegalMem = Legalizer.legalizeMemOperand(Src0M)) { |
| _lwc1(Dst, LegalMem); |
| CurInstr->setDeleted(); |
| } |
| continue; |
| } |
| if (llvm::isa<InstMIPS32Ldc1>(CurInstr)) { |
| if (auto *LegalMem = Legalizer.legalizeMemOperand(Src0M)) { |
| _ldc1(Dst, LegalMem); |
| CurInstr->setDeleted(); |
| } |
| continue; |
| } |
| if (auto *AddiuInstr = llvm::dyn_cast<InstMIPS32Addiu>(CurInstr)) { |
| if (auto *LegalImm = Legalizer.legalizeImmediate( |
| static_cast<int32_t>(AddiuInstr->getImmediateValue()))) { |
| _addu(Dst, Src0V, LegalImm); |
| CurInstr->setDeleted(); |
| } |
| continue; |
| } |
| } |
| } |
| } |
| |
| Operand *TargetMIPS32::loOperand(Operand *Operand) { |
| assert(Operand->getType() == IceType_i64); |
| if (auto *Var64On32 = llvm::dyn_cast<Variable64On32>(Operand)) |
| return Var64On32->getLo(); |
| if (auto *Const = llvm::dyn_cast<ConstantInteger64>(Operand)) { |
| return Ctx->getConstantInt32(static_cast<uint32_t>(Const->getValue())); |
| } |
| if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(Operand)) { |
| // Conservatively disallow memory operands with side-effects (pre/post |
| // increment) in case of duplication. |
| assert(Mem->getAddrMode() == OperandMIPS32Mem::Offset); |
| return OperandMIPS32Mem::create(Func, IceType_i32, Mem->getBase(), |
| Mem->getOffset(), Mem->getAddrMode()); |
| } |
| llvm_unreachable("Unsupported operand type"); |
| return nullptr; |
| } |
| |
| Operand *TargetMIPS32::getOperandAtIndex(Operand *Operand, Type BaseType, |
| uint32_t Index) { |
| if (!isVectorType(Operand->getType())) { |
| llvm::report_fatal_error("getOperandAtIndex: Operand is not vector"); |
| return nullptr; |
| } |
| |
| if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(Operand)) { |
| assert(Mem->getAddrMode() == OperandMIPS32Mem::Offset); |
| Variable *Base = Mem->getBase(); |
| auto *Offset = llvm::cast<ConstantInteger32>(Mem->getOffset()); |
| assert(!Utils::WouldOverflowAdd(Offset->getValue(), 4)); |
| int32_t NextOffsetVal = |
| Offset->getValue() + (Index * typeWidthInBytes(BaseType)); |
| constexpr bool NoSignExt = false; |
| if (!OperandMIPS32Mem::canHoldOffset(BaseType, NoSignExt, NextOffsetVal)) { |
| Constant *_4 = Ctx->getConstantInt32(4); |
| Variable *NewBase = Func->makeVariable(Base->getType()); |
| lowerArithmetic( |
| InstArithmetic::create(Func, InstArithmetic::Add, NewBase, Base, _4)); |
| Base = NewBase; |
| } else { |
| Offset = |
| llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(NextOffsetVal)); |
| } |
| return OperandMIPS32Mem::create(Func, BaseType, Base, Offset, |
| Mem->getAddrMode()); |
| } |
| |
| if (auto *VarVecOn32 = llvm::dyn_cast<VariableVecOn32>(Operand)) |
| return VarVecOn32->getContainers()[Index]; |
| |
| llvm_unreachable("Unsupported operand type"); |
| return nullptr; |
| } |
| |
| Operand *TargetMIPS32::hiOperand(Operand *Operand) { |
| assert(Operand->getType() == IceType_i64); |
| if (Operand->getType() != IceType_i64) |
| return Operand; |
| if (auto *Var64On32 = llvm::dyn_cast<Variable64On32>(Operand)) |
| return Var64On32->getHi(); |
| if (auto *Const = llvm::dyn_cast<ConstantInteger64>(Operand)) { |
| return Ctx->getConstantInt32( |
| static_cast<uint32_t>(Const->getValue() >> 32)); |
| } |
| if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(Operand)) { |
| // Conservatively disallow memory operands with side-effects |
| // in case of duplication. |
| assert(Mem->getAddrMode() == OperandMIPS32Mem::Offset); |
| const Type SplitType = IceType_i32; |
| Variable *Base = Mem->getBase(); |
| auto *Offset = llvm::cast<ConstantInteger32>(Mem->getOffset()); |
| assert(!Utils::WouldOverflowAdd(Offset->getValue(), 4)); |
| int32_t NextOffsetVal = Offset->getValue() + 4; |
| constexpr bool SignExt = false; |
| if (!OperandMIPS32Mem::canHoldOffset(SplitType, SignExt, NextOffsetVal)) { |
| // We have to make a temp variable and add 4 to either Base or Offset. |
| // If we add 4 to Offset, this will convert a non-RegReg addressing |
| // mode into a RegReg addressing mode. Since NaCl sandboxing disallows |
| // RegReg addressing modes, prefer adding to base and replacing instead. |
| // Thus we leave the old offset alone. |
| Constant *Four = Ctx->getConstantInt32(4); |
| Variable *NewBase = Func->makeVariable(Base->getType()); |
| lowerArithmetic(InstArithmetic::create(Func, InstArithmetic::Add, NewBase, |
| Base, Four)); |
| Base = NewBase; |
| } else { |
| Offset = |
| llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(NextOffsetVal)); |
| } |
| return OperandMIPS32Mem::create(Func, SplitType, Base, Offset, |
| Mem->getAddrMode()); |
| } |
| llvm_unreachable("Unsupported operand type"); |
| return nullptr; |
| } |
| |
| SmallBitVector TargetMIPS32::getRegisterSet(RegSetMask Include, |
| RegSetMask Exclude) const { |
| SmallBitVector Registers(RegMIPS32::Reg_NUM); |
| |
| #define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \ |
| isI64Pair, isFP32, isFP64, isVec128, alias_init) \ |
| if (scratch && (Include & RegSet_CallerSave)) \ |
| Registers[RegMIPS32::val] = true; \ |
| if (preserved && (Include & RegSet_CalleeSave)) \ |
| Registers[RegMIPS32::val] = true; \ |
| if (stackptr && (Include & RegSet_StackPointer)) \ |
| Registers[RegMIPS32::val] = true; \ |
| if (frameptr && (Include & RegSet_FramePointer)) \ |
| Registers[RegMIPS32::val] = true; \ |
| if (scratch && (Exclude & RegSet_CallerSave)) \ |
| Registers[RegMIPS32::val] = false; \ |
| if (preserved && (Exclude & RegSet_CalleeSave)) \ |
| Registers[RegMIPS32::val] = false; \ |
| if (stackptr && (Exclude & RegSet_StackPointer)) \ |
| Registers[RegMIPS32::val] = false; \ |
| if (frameptr && (Exclude & RegSet_FramePointer)) \ |
| Registers[RegMIPS32::val] = false; |
| |
| REGMIPS32_TABLE |
| |
| #undef X |
| |
| if (NeedSandboxing) { |
| Registers[RegMIPS32::Reg_T6] = false; |
| Registers[RegMIPS32::Reg_T7] = false; |
| Registers[RegMIPS32::Reg_T8] = false; |
| } |
| return Registers; |
| } |
| |
| void TargetMIPS32::lowerAlloca(const InstAlloca *Instr) { |
| // Conservatively require the stack to be aligned. Some stack adjustment |
| // operations implemented below assume that the stack is aligned before the |
| // alloca. All the alloca code ensures that the stack alignment is preserved |
| // after the alloca. The stack alignment restriction can be relaxed in some |
| // cases. |
| NeedsStackAlignment = true; |
| |
| // For default align=0, set it to the real value 1, to avoid any |
| // bit-manipulation problems below. |
| const uint32_t AlignmentParam = std::max(1u, Instr->getAlignInBytes()); |
| |
| // LLVM enforces power of 2 alignment. |
| assert(llvm::isPowerOf2_32(AlignmentParam)); |
| assert(llvm::isPowerOf2_32(MIPS32_STACK_ALIGNMENT_BYTES)); |
| |
| const uint32_t Alignment = |
| std::max(AlignmentParam, MIPS32_STACK_ALIGNMENT_BYTES); |
| const bool OverAligned = Alignment > MIPS32_STACK_ALIGNMENT_BYTES; |
| const bool OptM1 = Func->getOptLevel() == Opt_m1; |
| const bool AllocaWithKnownOffset = Instr->getKnownFrameOffset(); |
| const bool UseFramePointer = |
| hasFramePointer() || OverAligned || !AllocaWithKnownOffset || OptM1; |
| |
| if (UseFramePointer) |
| setHasFramePointer(); |
| |
| Variable *SP = getPhysicalRegister(RegMIPS32::Reg_SP); |
| |
| Variable *Dest = Instr->getDest(); |
| Operand *TotalSize = Instr->getSizeInBytes(); |
| |
| if (const auto *ConstantTotalSize = |
| llvm::dyn_cast<ConstantInteger32>(TotalSize)) { |
| const uint32_t Value = |
| Utils::applyAlignment(ConstantTotalSize->getValue(), Alignment); |
| FixedAllocaSizeBytes += Value; |
| // Constant size alloca. |
| if (!UseFramePointer) { |
| // If we don't need a Frame Pointer, this alloca has a known offset to the |
| // stack pointer. We don't need adjust the stack pointer, nor assign any |
| // value to Dest, as Dest is rematerializable. |
| assert(Dest->isRematerializable()); |
| Context.insert<InstFakeDef>(Dest); |
| return; |
| } |
| |
| if (Alignment > MIPS32_STACK_ALIGNMENT_BYTES) { |
| CurrentAllocaOffset = |
| Utils::applyAlignment(CurrentAllocaOffset, Alignment); |
| } |
| auto *T = I32Reg(); |
| _addiu(T, SP, CurrentAllocaOffset); |
| _mov(Dest, T); |
| CurrentAllocaOffset += Value; |
| return; |
| |
| } else { |
| // Non-constant sizes need to be adjusted to the next highest multiple of |
| // the required alignment at runtime. |
| VariableAllocaUsed = true; |
| VariableAllocaAlignBytes = AlignmentParam; |
| Variable *AlignAmount; |
| auto *TotalSizeR = legalizeToReg(TotalSize, Legal_Reg); |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| _addiu(T1, TotalSizeR, MIPS32_STACK_ALIGNMENT_BYTES - 1); |
| _addiu(T2, getZero(), -MIPS32_STACK_ALIGNMENT_BYTES); |
| _and(T3, T1, T2); |
| _subu(T4, SP, T3); |
| if (Instr->getAlignInBytes()) { |
| AlignAmount = |
| legalizeToReg(Ctx->getConstantInt32(-AlignmentParam), Legal_Reg); |
| _and(T5, T4, AlignAmount); |
| _mov(Dest, T5); |
| } else { |
| _mov(Dest, T4); |
| } |
| if (OptM1) |
| _mov(SP, Dest); |
| else |
| Sandboxer(this).reset_sp(Dest); |
| return; |
| } |
| } |
| |
| void TargetMIPS32::lowerInt64Arithmetic(const InstArithmetic *Instr, |
| Variable *Dest, Operand *Src0, |
| Operand *Src1) { |
| InstArithmetic::OpKind Op = Instr->getOp(); |
| auto *DestLo = llvm::cast<Variable>(loOperand(Dest)); |
| auto *DestHi = llvm::cast<Variable>(hiOperand(Dest)); |
| Variable *Src0LoR = nullptr; |
| Variable *Src1LoR = nullptr; |
| Variable *Src0HiR = nullptr; |
| Variable *Src1HiR = nullptr; |
| |
| switch (Op) { |
| case InstArithmetic::_num: |
| llvm::report_fatal_error("Unknown arithmetic operator"); |
| return; |
| case InstArithmetic::Add: { |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| Src1LoR = legalizeToReg(loOperand(Src1)); |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| Src1HiR = legalizeToReg(hiOperand(Src1)); |
| auto *T_Carry = I32Reg(), *T_Lo = I32Reg(), *T_Hi = I32Reg(), |
| *T_Hi2 = I32Reg(); |
| _addu(T_Lo, Src0LoR, Src1LoR); |
| _mov(DestLo, T_Lo); |
| _sltu(T_Carry, T_Lo, Src0LoR); |
| _addu(T_Hi, T_Carry, Src0HiR); |
| _addu(T_Hi2, Src1HiR, T_Hi); |
| _mov(DestHi, T_Hi2); |
| return; |
| } |
| case InstArithmetic::And: { |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| Src1LoR = legalizeToReg(loOperand(Src1)); |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| Src1HiR = legalizeToReg(hiOperand(Src1)); |
| auto *T_Lo = I32Reg(), *T_Hi = I32Reg(); |
| _and(T_Lo, Src0LoR, Src1LoR); |
| _mov(DestLo, T_Lo); |
| _and(T_Hi, Src0HiR, Src1HiR); |
| _mov(DestHi, T_Hi); |
| return; |
| } |
| case InstArithmetic::Sub: { |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| Src1LoR = legalizeToReg(loOperand(Src1)); |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| Src1HiR = legalizeToReg(hiOperand(Src1)); |
| auto *T_Borrow = I32Reg(), *T_Lo = I32Reg(), *T_Hi = I32Reg(), |
| *T_Hi2 = I32Reg(); |
| _subu(T_Lo, Src0LoR, Src1LoR); |
| _mov(DestLo, T_Lo); |
| _sltu(T_Borrow, Src0LoR, Src1LoR); |
| _addu(T_Hi, T_Borrow, Src1HiR); |
| _subu(T_Hi2, Src0HiR, T_Hi); |
| _mov(DestHi, T_Hi2); |
| return; |
| } |
| case InstArithmetic::Or: { |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| Src1LoR = legalizeToReg(loOperand(Src1)); |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| Src1HiR = legalizeToReg(hiOperand(Src1)); |
| auto *T_Lo = I32Reg(), *T_Hi = I32Reg(); |
| _or(T_Lo, Src0LoR, Src1LoR); |
| _mov(DestLo, T_Lo); |
| _or(T_Hi, Src0HiR, Src1HiR); |
| _mov(DestHi, T_Hi); |
| return; |
| } |
| case InstArithmetic::Xor: { |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| Src1LoR = legalizeToReg(loOperand(Src1)); |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| Src1HiR = legalizeToReg(hiOperand(Src1)); |
| auto *T_Lo = I32Reg(), *T_Hi = I32Reg(); |
| _xor(T_Lo, Src0LoR, Src1LoR); |
| _mov(DestLo, T_Lo); |
| _xor(T_Hi, Src0HiR, Src1HiR); |
| _mov(DestHi, T_Hi); |
| return; |
| } |
| case InstArithmetic::Mul: { |
| // TODO(rkotler): Make sure that mul has the side effect of clobbering |
| // LO, HI. Check for any other LO, HI quirkiness in this section. |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| Src1LoR = legalizeToReg(loOperand(Src1)); |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| Src1HiR = legalizeToReg(hiOperand(Src1)); |
| auto *T_Lo = I32Reg(RegMIPS32::Reg_LO), *T_Hi = I32Reg(RegMIPS32::Reg_HI); |
| auto *T1 = I32Reg(), *T2 = I32Reg(); |
| auto *TM1 = I32Reg(), *TM2 = I32Reg(), *TM3 = I32Reg(), *TM4 = I32Reg(); |
| _multu(T_Lo, Src0LoR, Src1LoR); |
| Context.insert<InstFakeDef>(T_Hi, T_Lo); |
| _mflo(T1, T_Lo); |
| _mfhi(T2, T_Hi); |
| _mov(DestLo, T1); |
| _mul(TM1, Src0HiR, Src1LoR); |
| _mul(TM2, Src0LoR, Src1HiR); |
| _addu(TM3, TM1, T2); |
| _addu(TM4, TM3, TM2); |
| _mov(DestHi, TM4); |
| return; |
| } |
| case InstArithmetic::Shl: { |
| auto *T_Lo = I32Reg(); |
| auto *T_Hi = I32Reg(); |
| auto *T1_Lo = I32Reg(); |
| auto *T1_Hi = I32Reg(); |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| |
| if (auto *Const = llvm::dyn_cast<ConstantInteger64>(Src1)) { |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| int64_t ShiftAmount = Const->getValue(); |
| if (ShiftAmount == 1) { |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| _addu(T_Lo, Src0LoR, Src0LoR); |
| _sltu(T1, T_Lo, Src0LoR); |
| _addu(T2, T1, Src0HiR); |
| _addu(T_Hi, Src0HiR, T2); |
| } else if (ShiftAmount < INT32_BITS) { |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| _srl(T1, Src0LoR, INT32_BITS - ShiftAmount); |
| _sll(T2, Src0HiR, ShiftAmount); |
| _or(T_Hi, T1, T2); |
| _sll(T_Lo, Src0LoR, ShiftAmount); |
| } else if (ShiftAmount == INT32_BITS) { |
| _addiu(T_Lo, getZero(), 0); |
| _mov(T_Hi, Src0LoR); |
| } else if (ShiftAmount > INT32_BITS && ShiftAmount < 64) { |
| _sll(T_Hi, Src0LoR, ShiftAmount - INT32_BITS); |
| _addiu(T_Lo, getZero(), 0); |
| } |
| _mov(DestLo, T_Lo); |
| _mov(DestHi, T_Hi); |
| return; |
| } |
| |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| Src1LoR = legalizeToReg(loOperand(Src1)); |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| |
| _sllv(T1, Src0HiR, Src1LoR); |
| _not(T2, Src1LoR); |
| _srl(T3, Src0LoR, 1); |
| _srlv(T4, T3, T2); |
| _or(T_Hi, T1, T4); |
| _sllv(T_Lo, Src0LoR, Src1LoR); |
| |
| _mov(T1_Hi, T_Hi); |
| _mov(T1_Lo, T_Lo); |
| _andi(T5, Src1LoR, INT32_BITS); |
| _movn(T1_Hi, T_Lo, T5); |
| _movn(T1_Lo, getZero(), T5); |
| _mov(DestHi, T1_Hi); |
| _mov(DestLo, T1_Lo); |
| return; |
| } |
| case InstArithmetic::Lshr: { |
| |
| auto *T_Lo = I32Reg(); |
| auto *T_Hi = I32Reg(); |
| auto *T1_Lo = I32Reg(); |
| auto *T1_Hi = I32Reg(); |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| |
| if (auto *Const = llvm::dyn_cast<ConstantInteger64>(Src1)) { |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| int64_t ShiftAmount = Const->getValue(); |
| if (ShiftAmount < INT32_BITS) { |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| _sll(T1, Src0HiR, INT32_BITS - ShiftAmount); |
| _srl(T2, Src0LoR, ShiftAmount); |
| _or(T_Lo, T1, T2); |
| _srl(T_Hi, Src0HiR, ShiftAmount); |
| } else if (ShiftAmount == INT32_BITS) { |
| _mov(T_Lo, Src0HiR); |
| _addiu(T_Hi, getZero(), 0); |
| } else if (ShiftAmount > INT32_BITS && ShiftAmount < 64) { |
| _srl(T_Lo, Src0HiR, ShiftAmount - INT32_BITS); |
| _addiu(T_Hi, getZero(), 0); |
| } |
| _mov(DestLo, T_Lo); |
| _mov(DestHi, T_Hi); |
| return; |
| } |
| |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| Src1LoR = legalizeToReg(loOperand(Src1)); |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| |
| _srlv(T1, Src0LoR, Src1LoR); |
| _not(T2, Src1LoR); |
| _sll(T3, Src0HiR, 1); |
| _sllv(T4, T3, T2); |
| _or(T_Lo, T1, T4); |
| _srlv(T_Hi, Src0HiR, Src1LoR); |
| |
| _mov(T1_Hi, T_Hi); |
| _mov(T1_Lo, T_Lo); |
| _andi(T5, Src1LoR, INT32_BITS); |
| _movn(T1_Lo, T_Hi, T5); |
| _movn(T1_Hi, getZero(), T5); |
| _mov(DestHi, T1_Hi); |
| _mov(DestLo, T1_Lo); |
| return; |
| } |
| case InstArithmetic::Ashr: { |
| |
| auto *T_Lo = I32Reg(); |
| auto *T_Hi = I32Reg(); |
| auto *T1_Lo = I32Reg(); |
| auto *T1_Hi = I32Reg(); |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| auto *T6 = I32Reg(); |
| |
| if (auto *Const = llvm::dyn_cast<ConstantInteger64>(Src1)) { |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| int64_t ShiftAmount = Const->getValue(); |
| if (ShiftAmount < INT32_BITS) { |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| _sll(T1, Src0HiR, INT32_BITS - ShiftAmount); |
| _srl(T2, Src0LoR, ShiftAmount); |
| _or(T_Lo, T1, T2); |
| _sra(T_Hi, Src0HiR, ShiftAmount); |
| } else if (ShiftAmount == INT32_BITS) { |
| _sra(T_Hi, Src0HiR, INT32_BITS - 1); |
| _mov(T_Lo, Src0HiR); |
| } else if (ShiftAmount > INT32_BITS && ShiftAmount < 64) { |
| _sra(T_Lo, Src0HiR, ShiftAmount - INT32_BITS); |
| _sra(T_Hi, Src0HiR, INT32_BITS - 1); |
| } |
| _mov(DestLo, T_Lo); |
| _mov(DestHi, T_Hi); |
| return; |
| } |
| |
| Src0LoR = legalizeToReg(loOperand(Src0)); |
| Src1LoR = legalizeToReg(loOperand(Src1)); |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| |
| _srlv(T1, Src0LoR, Src1LoR); |
| _not(T2, Src1LoR); |
| _sll(T3, Src0HiR, 1); |
| _sllv(T4, T3, T2); |
| _or(T_Lo, T1, T4); |
| _srav(T_Hi, Src0HiR, Src1LoR); |
| |
| _mov(T1_Hi, T_Hi); |
| _mov(T1_Lo, T_Lo); |
| _andi(T5, Src1LoR, INT32_BITS); |
| _movn(T1_Lo, T_Hi, T5); |
| _sra(T6, Src0HiR, INT32_BITS - 1); |
| _movn(T1_Hi, T6, T5); |
| _mov(DestHi, T1_Hi); |
| _mov(DestLo, T1_Lo); |
| return; |
| } |
| case InstArithmetic::Fadd: |
| case InstArithmetic::Fsub: |
| case InstArithmetic::Fmul: |
| case InstArithmetic::Fdiv: |
| case InstArithmetic::Frem: |
| llvm::report_fatal_error("FP instruction with i64 type"); |
| return; |
| case InstArithmetic::Udiv: |
| case InstArithmetic::Sdiv: |
| case InstArithmetic::Urem: |
| case InstArithmetic::Srem: |
| llvm::report_fatal_error("64-bit div and rem should have been prelowered"); |
| return; |
| } |
| } |
| |
| void TargetMIPS32::lowerArithmetic(const InstArithmetic *Instr) { |
| Variable *Dest = Instr->getDest(); |
| |
| if (Dest->isRematerializable()) { |
| Context.insert<InstFakeDef>(Dest); |
| return; |
| } |
| |
| // We need to signal all the UnimplementedLoweringError errors before any |
| // legalization into new variables, otherwise Om1 register allocation may fail |
| // when it sees variables that are defined but not used. |
| Type DestTy = Dest->getType(); |
| Operand *Src0 = legalizeUndef(Instr->getSrc(0)); |
| Operand *Src1 = legalizeUndef(Instr->getSrc(1)); |
| if (DestTy == IceType_i64) { |
| lowerInt64Arithmetic(Instr, Instr->getDest(), Src0, Src1); |
| return; |
| } |
| if (isVectorType(Dest->getType())) { |
| llvm::report_fatal_error("Arithmetic: Destination type is vector"); |
| return; |
| } |
| |
| Variable *T = makeReg(Dest->getType()); |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *Src1R = nullptr; |
| uint32_t Value = 0; |
| bool IsSrc1Imm16 = false; |
| |
| switch (Instr->getOp()) { |
| case InstArithmetic::Add: |
| case InstArithmetic::Sub: { |
| auto *Const32 = llvm::dyn_cast<ConstantInteger32>(Src1); |
| if (Const32 != nullptr && isInt<16>(int32_t(Const32->getValue()))) { |
| IsSrc1Imm16 = true; |
| Value = Const32->getValue(); |
| } else { |
| Src1R = legalizeToReg(Src1); |
| } |
| break; |
| } |
| case InstArithmetic::And: |
| case InstArithmetic::Or: |
| case InstArithmetic::Xor: |
| case InstArithmetic::Shl: |
| case InstArithmetic::Lshr: |
| case InstArithmetic::Ashr: { |
| auto *Const32 = llvm::dyn_cast<ConstantInteger32>(Src1); |
| if (Const32 != nullptr && llvm::isUInt<16>(uint32_t(Const32->getValue()))) { |
| IsSrc1Imm16 = true; |
| Value = Const32->getValue(); |
| } else { |
| Src1R = legalizeToReg(Src1); |
| } |
| break; |
| } |
| default: |
| Src1R = legalizeToReg(Src1); |
| break; |
| } |
| constexpr uint32_t DivideByZeroTrapCode = 7; |
| |
| switch (Instr->getOp()) { |
| case InstArithmetic::_num: |
| break; |
| case InstArithmetic::Add: { |
| auto *T0R = Src0R; |
| auto *T1R = Src1R; |
| if (Dest->getType() != IceType_i32) { |
| T0R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Sext, T0R, Src0R)); |
| if (!IsSrc1Imm16) { |
| T1R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Sext, T1R, Src1R)); |
| } |
| } |
| if (IsSrc1Imm16) { |
| _addiu(T, T0R, Value); |
| } else { |
| _addu(T, T0R, T1R); |
| } |
| _mov(Dest, T); |
| return; |
| } |
| case InstArithmetic::And: |
| if (IsSrc1Imm16) { |
| _andi(T, Src0R, Value); |
| } else { |
| _and(T, Src0R, Src1R); |
| } |
| _mov(Dest, T); |
| return; |
| case InstArithmetic::Or: |
| if (IsSrc1Imm16) { |
| _ori(T, Src0R, Value); |
| } else { |
| _or(T, Src0R, Src1R); |
| } |
| _mov(Dest, T); |
| return; |
| case InstArithmetic::Xor: |
| if (IsSrc1Imm16) { |
| _xori(T, Src0R, Value); |
| } else { |
| _xor(T, Src0R, Src1R); |
| } |
| _mov(Dest, T); |
| return; |
| case InstArithmetic::Sub: { |
| auto *T0R = Src0R; |
| auto *T1R = Src1R; |
| if (Dest->getType() != IceType_i32) { |
| T0R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Sext, T0R, Src0R)); |
| if (!IsSrc1Imm16) { |
| T1R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Sext, T1R, Src1R)); |
| } |
| } |
| if (IsSrc1Imm16) { |
| _addiu(T, T0R, -Value); |
| } else { |
| _subu(T, T0R, T1R); |
| } |
| _mov(Dest, T); |
| return; |
| } |
| case InstArithmetic::Mul: { |
| _mul(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| } |
| case InstArithmetic::Shl: { |
| if (IsSrc1Imm16) { |
| _sll(T, Src0R, Value); |
| } else { |
| _sllv(T, Src0R, Src1R); |
| } |
| _mov(Dest, T); |
| return; |
| } |
| case InstArithmetic::Lshr: { |
| auto *T0R = Src0R; |
| auto *T1R = Src1R; |
| if (Dest->getType() != IceType_i32) { |
| T0R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Zext, T0R, Src0R)); |
| if (!IsSrc1Imm16) { |
| T1R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Zext, T1R, Src1R)); |
| } |
| } |
| if (IsSrc1Imm16) { |
| _srl(T, T0R, Value); |
| } else { |
| _srlv(T, T0R, T1R); |
| } |
| _mov(Dest, T); |
| return; |
| } |
| case InstArithmetic::Ashr: { |
| auto *T0R = Src0R; |
| auto *T1R = Src1R; |
| if (Dest->getType() != IceType_i32) { |
| T0R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Sext, T0R, Src0R)); |
| if (!IsSrc1Imm16) { |
| T1R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Sext, T1R, Src1R)); |
| } |
| } |
| if (IsSrc1Imm16) { |
| _sra(T, T0R, Value); |
| } else { |
| _srav(T, T0R, T1R); |
| } |
| _mov(Dest, T); |
| return; |
| } |
| case InstArithmetic::Udiv: { |
| auto *T_Zero = I32Reg(RegMIPS32::Reg_ZERO); |
| auto *T0R = Src0R; |
| auto *T1R = Src1R; |
| if (Dest->getType() != IceType_i32) { |
| T0R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Zext, T0R, Src0R)); |
| T1R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Zext, T1R, Src1R)); |
| } |
| _divu(T_Zero, T0R, T1R); |
| _teq(T1R, T_Zero, DivideByZeroTrapCode); // Trap if divide-by-zero |
| _mflo(T, T_Zero); |
| _mov(Dest, T); |
| return; |
| } |
| case InstArithmetic::Sdiv: { |
| auto *T_Zero = I32Reg(RegMIPS32::Reg_ZERO); |
| auto *T0R = Src0R; |
| auto *T1R = Src1R; |
| if (Dest->getType() != IceType_i32) { |
| T0R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Sext, T0R, Src0R)); |
| T1R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Sext, T1R, Src1R)); |
| } |
| _div(T_Zero, T0R, T1R); |
| _teq(T1R, T_Zero, DivideByZeroTrapCode); // Trap if divide-by-zero |
| _mflo(T, T_Zero); |
| _mov(Dest, T); |
| return; |
| } |
| case InstArithmetic::Urem: { |
| auto *T_Zero = I32Reg(RegMIPS32::Reg_ZERO); |
| auto *T0R = Src0R; |
| auto *T1R = Src1R; |
| if (Dest->getType() != IceType_i32) { |
| T0R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Zext, T0R, Src0R)); |
| T1R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Zext, T1R, Src1R)); |
| } |
| _divu(T_Zero, T0R, T1R); |
| _teq(T1R, T_Zero, DivideByZeroTrapCode); // Trap if divide-by-zero |
| _mfhi(T, T_Zero); |
| _mov(Dest, T); |
| return; |
| } |
| case InstArithmetic::Srem: { |
| auto *T_Zero = I32Reg(RegMIPS32::Reg_ZERO); |
| auto *T0R = Src0R; |
| auto *T1R = Src1R; |
| if (Dest->getType() != IceType_i32) { |
| T0R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Sext, T0R, Src0R)); |
| T1R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, InstCast::Sext, T1R, Src1R)); |
| } |
| _div(T_Zero, T0R, T1R); |
| _teq(T1R, T_Zero, DivideByZeroTrapCode); // Trap if divide-by-zero |
| _mfhi(T, T_Zero); |
| _mov(Dest, T); |
| return; |
| } |
| case InstArithmetic::Fadd: { |
| if (DestTy == IceType_f32) { |
| _add_s(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| } |
| if (DestTy == IceType_f64) { |
| _add_d(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| } |
| break; |
| } |
| case InstArithmetic::Fsub: |
| if (DestTy == IceType_f32) { |
| _sub_s(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| } |
| if (DestTy == IceType_f64) { |
| _sub_d(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| } |
| break; |
| case InstArithmetic::Fmul: |
| if (DestTy == IceType_f32) { |
| _mul_s(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| } |
| if (DestTy == IceType_f64) { |
| _mul_d(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| } |
| break; |
| case InstArithmetic::Fdiv: |
| if (DestTy == IceType_f32) { |
| _div_s(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| } |
| if (DestTy == IceType_f64) { |
| _div_d(T, Src0R, Src1R); |
| _mov(Dest, T); |
| return; |
| } |
| break; |
| case InstArithmetic::Frem: |
| llvm::report_fatal_error("frem should have been prelowered."); |
| break; |
| } |
| llvm::report_fatal_error("Unknown arithmetic operator"); |
| } |
| |
| void TargetMIPS32::lowerAssign(const InstAssign *Instr) { |
| Variable *Dest = Instr->getDest(); |
| |
| if (Dest->isRematerializable()) { |
| Context.insert<InstFakeDef>(Dest); |
| return; |
| } |
| |
| // Source type may not be same as destination |
| if (isVectorType(Dest->getType())) { |
| Operand *Src0 = legalizeUndef(Instr->getSrc(0)); |
| auto *DstVec = llvm::dyn_cast<VariableVecOn32>(Dest); |
| for (SizeT i = 0; i < DstVec->ContainersPerVector; ++i) { |
| auto *DCont = DstVec->getContainers()[i]; |
| auto *SCont = |
| legalize(getOperandAtIndex(Src0, IceType_i32, i), Legal_Reg); |
| auto *TReg = makeReg(IceType_i32); |
| _mov(TReg, SCont); |
| _mov(DCont, TReg); |
| } |
| return; |
| } |
| Operand *Src0 = Instr->getSrc(0); |
| assert(Dest->getType() == Src0->getType()); |
| if (Dest->getType() == IceType_i64) { |
| Src0 = legalizeUndef(Src0); |
| Operand *Src0Lo = legalize(loOperand(Src0), Legal_Reg); |
| Operand *Src0Hi = legalize(hiOperand(Src0), Legal_Reg); |
| auto *DestLo = llvm::cast<Variable>(loOperand(Dest)); |
| auto *DestHi = llvm::cast<Variable>(hiOperand(Dest)); |
| auto *T_Lo = I32Reg(), *T_Hi = I32Reg(); |
| _mov(T_Lo, Src0Lo); |
| _mov(DestLo, T_Lo); |
| _mov(T_Hi, Src0Hi); |
| _mov(DestHi, T_Hi); |
| return; |
| } |
| Operand *SrcR; |
| if (Dest->hasReg()) { |
| // If Dest already has a physical register, then legalize the Src operand |
| // into a Variable with the same register assignment. This especially |
| // helps allow the use of Flex operands. |
| SrcR = legalize(Src0, Legal_Reg, Dest->getRegNum()); |
| } else { |
| // Dest could be a stack operand. Since we could potentially need |
| // to do a Store (and store can only have Register operands), |
| // legalize this to a register. |
| SrcR = legalize(Src0, Legal_Reg); |
| } |
| _mov(Dest, SrcR); |
| } |
| |
| void TargetMIPS32::lowerBr(const InstBr *Instr) { |
| if (Instr->isUnconditional()) { |
| _br(Instr->getTargetUnconditional()); |
| return; |
| } |
| CfgNode *TargetTrue = Instr->getTargetTrue(); |
| CfgNode *TargetFalse = Instr->getTargetFalse(); |
| Operand *Boolean = Instr->getCondition(); |
| const Inst *Producer = Computations.getProducerOf(Boolean); |
| if (Producer == nullptr) { |
| // Since we don't know the producer of this boolean we will assume its |
| // producer will keep it in positive logic and just emit beqz with this |
| // Boolean as an operand. |
| auto *BooleanR = legalizeToReg(Boolean); |
| _br(TargetTrue, TargetFalse, BooleanR, CondMIPS32::Cond::EQZ); |
| return; |
| } |
| if (Producer->getKind() == Inst::Icmp) { |
| const InstIcmp *CompareInst = llvm::cast<InstIcmp>(Producer); |
| Operand *Src0 = CompareInst->getSrc(0); |
| Operand *Src1 = CompareInst->getSrc(1); |
| const Type Src0Ty = Src0->getType(); |
| assert(Src0Ty == Src1->getType()); |
| |
| Variable *Src0R = nullptr; |
| Variable *Src1R = nullptr; |
| Variable *Src0HiR = nullptr; |
| Variable *Src1HiR = nullptr; |
| if (Src0Ty == IceType_i64) { |
| Src0R = legalizeToReg(loOperand(Src0)); |
| Src1R = legalizeToReg(loOperand(Src1)); |
| Src0HiR = legalizeToReg(hiOperand(Src0)); |
| Src1HiR = legalizeToReg(hiOperand(Src1)); |
| } else { |
| auto *Src0RT = legalizeToReg(Src0); |
| auto *Src1RT = legalizeToReg(Src1); |
| // Sign/Zero extend the source operands |
| if (Src0Ty != IceType_i32) { |
| InstCast::OpKind CastKind; |
| switch (CompareInst->getCondition()) { |
| case InstIcmp::Eq: |
| case InstIcmp::Ne: |
| case InstIcmp::Sgt: |
| case InstIcmp::Sge: |
| case InstIcmp::Slt: |
| case InstIcmp::Sle: |
| CastKind = InstCast::Sext; |
| break; |
| default: |
| CastKind = InstCast::Zext; |
| break; |
| } |
| Src0R = makeReg(IceType_i32); |
| Src1R = makeReg(IceType_i32); |
| lowerCast(InstCast::create(Func, CastKind, Src0R, Src0RT)); |
| lowerCast(InstCast::create(Func, CastKind, Src1R, Src1RT)); |
| } else { |
| Src0R = Src0RT; |
| Src1R = Src1RT; |
| } |
| } |
| auto *DestT = makeReg(IceType_i32); |
| |
| switch (CompareInst->getCondition()) { |
| default: |
| llvm_unreachable("unexpected condition"); |
| return; |
| case InstIcmp::Eq: { |
| if (Src0Ty == IceType_i64) { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _xor(T2, Src0R, Src1R); |
| _or(T3, T1, T2); |
| _mov(DestT, T3); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } else { |
| _br(TargetTrue, TargetFalse, Src0R, Src1R, CondMIPS32::Cond::NE); |
| } |
| return; |
| } |
| case InstIcmp::Ne: { |
| if (Src0Ty == IceType_i64) { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _xor(T2, Src0R, Src1R); |
| _or(T3, T1, T2); |
| _mov(DestT, T3); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::EQZ); |
| } else { |
| _br(TargetTrue, TargetFalse, Src0R, Src1R, CondMIPS32::Cond::EQ); |
| } |
| return; |
| } |
| case InstIcmp::Ugt: { |
| if (Src0Ty == IceType_i64) { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _sltu(T2, Src1HiR, Src0HiR); |
| _xori(T3, T2, 1); |
| _sltu(T4, Src1R, Src0R); |
| _xori(T5, T4, 1); |
| _movz(T3, T5, T1); |
| _mov(DestT, T3); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } else { |
| _sltu(DestT, Src1R, Src0R); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::EQZ); |
| } |
| return; |
| } |
| case InstIcmp::Uge: { |
| if (Src0Ty == IceType_i64) { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _sltu(T2, Src0HiR, Src1HiR); |
| _sltu(T3, Src0R, Src1R); |
| _movz(T2, T3, T1); |
| _mov(DestT, T2); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } else { |
| _sltu(DestT, Src0R, Src1R); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } |
| return; |
| } |
| case InstIcmp::Ult: { |
| if (Src0Ty == IceType_i64) { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _sltu(T2, Src0HiR, Src1HiR); |
| _xori(T3, T2, 1); |
| _sltu(T4, Src0R, Src1R); |
| _xori(T5, T4, 1); |
| _movz(T3, T5, T1); |
| _mov(DestT, T3); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } else { |
| _sltu(DestT, Src0R, Src1R); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::EQZ); |
| } |
| return; |
| } |
| case InstIcmp::Ule: { |
| if (Src0Ty == IceType_i64) { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _sltu(T2, Src1HiR, Src0HiR); |
| _sltu(T3, Src1R, Src0R); |
| _movz(T2, T3, T1); |
| _mov(DestT, T2); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } else { |
| _sltu(DestT, Src1R, Src0R); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } |
| return; |
| } |
| case InstIcmp::Sgt: { |
| if (Src0Ty == IceType_i64) { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _slt(T2, Src1HiR, Src0HiR); |
| _xori(T3, T2, 1); |
| _sltu(T4, Src1R, Src0R); |
| _xori(T5, T4, 1); |
| _movz(T3, T5, T1); |
| _mov(DestT, T3); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } else { |
| _slt(DestT, Src1R, Src0R); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::EQZ); |
| } |
| return; |
| } |
| case InstIcmp::Sge: { |
| if (Src0Ty == IceType_i64) { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _slt(T2, Src0HiR, Src1HiR); |
| _sltu(T3, Src0R, Src1R); |
| _movz(T2, T3, T1); |
| _mov(DestT, T2); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } else { |
| _slt(DestT, Src0R, Src1R); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } |
| return; |
| } |
| case InstIcmp::Slt: { |
| if (Src0Ty == IceType_i64) { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _slt(T2, Src0HiR, Src1HiR); |
| _xori(T3, T2, 1); |
| _sltu(T4, Src0R, Src1R); |
| _xori(T5, T4, 1); |
| _movz(T3, T5, T1); |
| _mov(DestT, T3); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } else { |
| _slt(DestT, Src0R, Src1R); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::EQZ); |
| } |
| return; |
| } |
| case InstIcmp::Sle: { |
| if (Src0Ty == IceType_i64) { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _slt(T2, Src1HiR, Src0HiR); |
| _sltu(T3, Src1R, Src0R); |
| _movz(T2, T3, T1); |
| _mov(DestT, T2); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } else { |
| _slt(DestT, Src1R, Src0R); |
| _br(TargetTrue, TargetFalse, DestT, CondMIPS32::Cond::NEZ); |
| } |
| return; |
| } |
| } |
| } |
| } |
| |
| void TargetMIPS32::lowerCall(const InstCall *Instr) { |
| CfgVector<Variable *> RegArgs; |
| NeedsStackAlignment = true; |
| |
| // Assign arguments to registers and stack. Also reserve stack. |
| TargetMIPS32::CallingConv CC; |
| |
| // Pair of Arg Operand -> GPR number assignments. |
| llvm::SmallVector<std::pair<Operand *, RegNumT>, MIPS32_MAX_GPR_ARG> GPRArgs; |
| llvm::SmallVector<std::pair<Operand *, RegNumT>, MIPS32_MAX_FP_ARG> FPArgs; |
| // Pair of Arg Operand -> stack offset. |
| llvm::SmallVector<std::pair<Operand *, int32_t>, 8> StackArgs; |
| size_t ParameterAreaSizeBytes = 16; |
| |
| // Classify each argument operand according to the location where the |
| // argument is passed. |
| |
| // v4f32 is returned through stack. $4 is setup by the caller and passed as |
| // first argument implicitly. Callee then copies the return vector at $4. |
| SizeT ArgNum = 0; |
| Variable *Dest = Instr->getDest(); |
| Variable *RetVecFloat = nullptr; |
| if (Dest && isVectorFloatingType(Dest->getType())) { |
| ArgNum = 1; |
| CC.discardReg(RegMIPS32::Reg_A0); |
| RetVecFloat = Func->makeVariable(IceType_i32); |
| auto *ByteCount = ConstantInteger32::create(Ctx, IceType_i32, 16); |
| constexpr SizeT Alignment = 4; |
| lowerAlloca(InstAlloca::create(Func, RetVecFloat, ByteCount, Alignment)); |
| RegArgs.emplace_back( |
| legalizeToReg(RetVecFloat, RegNumT::fixme(RegMIPS32::Reg_A0))); |
| } |
| |
| for (SizeT i = 0, NumArgs = Instr->getNumArgs(); i < NumArgs; ++i) { |
| Operand *Arg = legalizeUndef(Instr->getArg(i)); |
| const Type Ty = Arg->getType(); |
| bool InReg = false; |
| RegNumT Reg; |
| |
| InReg = CC.argInReg(Ty, i, &Reg); |
| |
| if (!InReg) { |
| if (isVectorType(Ty)) { |
| auto *ArgVec = llvm::cast<VariableVecOn32>(Arg); |
| ParameterAreaSizeBytes = |
| applyStackAlignmentTy(ParameterAreaSizeBytes, IceType_i64); |
| for (Variable *Elem : ArgVec->getContainers()) { |
| StackArgs.push_back(std::make_pair(Elem, ParameterAreaSizeBytes)); |
| ParameterAreaSizeBytes += typeWidthInBytesOnStack(IceType_i32); |
| } |
| } else { |
| ParameterAreaSizeBytes = |
| applyStackAlignmentTy(ParameterAreaSizeBytes, Ty); |
| StackArgs.push_back(std::make_pair(Arg, ParameterAreaSizeBytes)); |
| ParameterAreaSizeBytes += typeWidthInBytesOnStack(Ty); |
| } |
| ++ArgNum; |
| continue; |
| } |
| |
| if (isVectorType(Ty)) { |
| auto *ArgVec = llvm::cast<VariableVecOn32>(Arg); |
| Operand *Elem0 = ArgVec->getContainers()[0]; |
| Operand *Elem1 = ArgVec->getContainers()[1]; |
| GPRArgs.push_back( |
| std::make_pair(Elem0, RegNumT::fixme((unsigned)Reg + 0))); |
| GPRArgs.push_back( |
| std::make_pair(Elem1, RegNumT::fixme((unsigned)Reg + 1))); |
| Operand *Elem2 = ArgVec->getContainers()[2]; |
| Operand *Elem3 = ArgVec->getContainers()[3]; |
| // First argument is passed in $4:$5:$6:$7 |
| // Second and rest arguments are passed in $6:$7:stack:stack |
| if (ArgNum == 0) { |
| GPRArgs.push_back( |
| std::make_pair(Elem2, RegNumT::fixme((unsigned)Reg + 2))); |
| GPRArgs.push_back( |
| std::make_pair(Elem3, RegNumT::fixme((unsigned)Reg + 3))); |
| } else { |
| ParameterAreaSizeBytes = |
| applyStackAlignmentTy(ParameterAreaSizeBytes, IceType_i64); |
| StackArgs.push_back(std::make_pair(Elem2, ParameterAreaSizeBytes)); |
| ParameterAreaSizeBytes += typeWidthInBytesOnStack(IceType_i32); |
| StackArgs.push_back(std::make_pair(Elem3, ParameterAreaSizeBytes)); |
| ParameterAreaSizeBytes += typeWidthInBytesOnStack(IceType_i32); |
| } |
| } else if (Ty == IceType_i64) { |
| Operand *Lo = loOperand(Arg); |
| Operand *Hi = hiOperand(Arg); |
| GPRArgs.push_back( |
| std::make_pair(Lo, RegMIPS32::get64PairFirstRegNum(Reg))); |
| GPRArgs.push_back( |
| std::make_pair(Hi, RegMIPS32::get64PairSecondRegNum(Reg))); |
| } else if (isScalarIntegerType(Ty)) { |
| GPRArgs.push_back(std::make_pair(Arg, Reg)); |
| } else { |
| FPArgs.push_back(std::make_pair(Arg, Reg)); |
| } |
| ++ArgNum; |
| } |
| |
| // Adjust the parameter area so that the stack is aligned. It is assumed that |
| // the stack is already aligned at the start of the calling sequence. |
| ParameterAreaSizeBytes = applyStackAlignment(ParameterAreaSizeBytes); |
| |
| // Copy arguments that are passed on the stack to the appropriate stack |
| // locations. |
| Variable *SP = getPhysicalRegister(RegMIPS32::Reg_SP); |
| for (auto &StackArg : StackArgs) { |
| ConstantInteger32 *Loc = |
| llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(StackArg.second)); |
| Type Ty = StackArg.first->getType(); |
| OperandMIPS32Mem *Addr; |
| constexpr bool SignExt = false; |
| if (OperandMIPS32Mem::canHoldOffset(Ty, SignExt, StackArg.second)) { |
| Addr = OperandMIPS32Mem::create(Func, Ty, SP, Loc); |
| } else { |
| Variable *NewBase = Func->makeVariable(SP->getType()); |
| lowerArithmetic( |
| InstArithmetic::create(Func, InstArithmetic::Add, NewBase, SP, Loc)); |
| Addr = formMemoryOperand(NewBase, Ty); |
| } |
| lowerStore(InstStore::create(Func, StackArg.first, Addr)); |
| } |
| |
| // Generate the call instruction. Assign its result to a temporary with high |
| // register allocation weight. |
| |
| // ReturnReg doubles as ReturnRegLo as necessary. |
| Variable *ReturnReg = nullptr; |
| Variable *ReturnRegHi = nullptr; |
| if (Dest) { |
| switch (Dest->getType()) { |
| case IceType_NUM: |
| llvm_unreachable("Invalid Call dest type"); |
| return; |
| case IceType_void: |
| break; |
| case IceType_i1: |
| case IceType_i8: |
| case IceType_i16: |
| case IceType_i32: |
| ReturnReg = makeReg(Dest->getType(), RegMIPS32::Reg_V0); |
| break; |
| case IceType_i64: |
| ReturnReg = I32Reg(RegMIPS32::Reg_V0); |
| ReturnRegHi = I32Reg(RegMIPS32::Reg_V1); |
| break; |
| case IceType_f32: |
| ReturnReg = makeReg(Dest->getType(), RegMIPS32::Reg_F0); |
| break; |
| case IceType_f64: |
| ReturnReg = makeReg(IceType_f64, RegMIPS32::Reg_F0); |
| break; |
| case IceType_v4i1: |
| case IceType_v8i1: |
| case IceType_v16i1: |
| case IceType_v16i8: |
| case IceType_v8i16: |
| case IceType_v4i32: { |
| ReturnReg = makeReg(Dest->getType(), RegMIPS32::Reg_V0); |
| auto *RetVec = llvm::dyn_cast<VariableVecOn32>(ReturnReg); |
| RetVec->initVecElement(Func); |
| for (SizeT i = 0; i < RetVec->ContainersPerVector; ++i) { |
| auto *Var = RetVec->getContainers()[i]; |
| Var->setRegNum(RegNumT::fixme(RegMIPS32::Reg_V0 + i)); |
| } |
| break; |
| } |
| case IceType_v4f32: |
| ReturnReg = makeReg(IceType_i32, RegMIPS32::Reg_V0); |
| break; |
| } |
| } |
| Operand *CallTarget = Instr->getCallTarget(); |
| // Allow ConstantRelocatable to be left alone as a direct call, |
| // but force other constants like ConstantInteger32 to be in |
| // a register and make it an indirect call. |
| if (!llvm::isa<ConstantRelocatable>(CallTarget)) { |
| CallTarget = legalize(CallTarget, Legal_Reg); |
| } |
| |
| // Copy arguments to be passed in registers to the appropriate registers. |
| for (auto &FPArg : FPArgs) { |
| RegArgs.emplace_back(legalizeToReg(FPArg.first, FPArg.second)); |
| } |
| for (auto &GPRArg : GPRArgs) { |
| RegArgs.emplace_back(legalizeToReg(GPRArg.first, GPRArg.second)); |
| } |
| |
| // Generate a FakeUse of register arguments so that they do not get dead code |
| // eliminated as a result of the FakeKill of scratch registers after the call. |
| // These fake-uses need to be placed here to avoid argument registers from |
| // being used during the legalizeToReg() calls above. |
| for (auto *RegArg : RegArgs) { |
| Context.insert<InstFakeUse>(RegArg); |
| } |
| |
| // If variable alloca is used the extra 16 bytes for argument build area |
| // will be allocated on stack before a call. |
| if (VariableAllocaUsed) |
| Sandboxer(this).addiu_sp(-MaxOutArgsSizeBytes); |
| |
| Inst *NewCall; |
| |
| // We don't need to define the return register if it is a vector. |
| // We have inserted fake defs of it just after the call. |
| if (ReturnReg && isVectorIntegerType(ReturnReg->getType())) { |
| Variable *RetReg = nullptr; |
| NewCall = InstMIPS32Call::create(Func, RetReg, CallTarget); |
| Context.insert(NewCall); |
| } else { |
| NewCall = Sandboxer(this, InstBundleLock::Opt_AlignToEnd) |
| .jal(ReturnReg, CallTarget); |
| } |
| |
| if (VariableAllocaUsed) |
| Sandboxer(this).addiu_sp(MaxOutArgsSizeBytes); |
| |
| // Insert a fake use of stack pointer to avoid dead code elimination of addiu |
| // instruction. |
| Context.insert<InstFakeUse>(SP); |
| |
| if (ReturnRegHi) |
| Context.insert(InstFakeDef::create(Func, ReturnRegHi)); |
| |
| if (ReturnReg) { |
| if (auto *RetVec = llvm::dyn_cast<VariableVecOn32>(ReturnReg)) { |
| for (Variable *Var : RetVec->getContainers()) { |
| Context.insert(InstFakeDef::create(Func, Var)); |
| } |
| } |
| } |
| |
| // Insert a register-kill pseudo instruction. |
| Context.insert(InstFakeKill::create(Func, NewCall)); |
| |
| // Generate a FakeUse to keep the call live if necessary. |
| if (Instr->hasSideEffects() && ReturnReg) { |
| if (auto *RetVec = llvm::dyn_cast<VariableVecOn32>(ReturnReg)) { |
| for (Variable *Var : RetVec->getContainers()) { |
| Context.insert<InstFakeUse>(Var); |
| } |
| } else { |
| Context.insert<InstFakeUse>(ReturnReg); |
| } |
| } |
| |
| if (Dest == nullptr) |
| return; |
| |
| // Assign the result of the call to Dest. |
| if (ReturnReg) { |
| if (RetVecFloat) { |
| auto *DestVecOn32 = llvm::cast<VariableVecOn32>(Dest); |
| auto *TBase = legalizeToReg(RetVecFloat); |
| for (SizeT i = 0; i < DestVecOn32->ContainersPerVector; ++i) { |
| auto *Var = DestVecOn32->getContainers()[i]; |
| auto *TVar = makeReg(IceType_i32); |
| OperandMIPS32Mem *Mem = OperandMIPS32Mem::create( |
| Func, IceType_i32, TBase, |
| llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(i * 4))); |
| _lw(TVar, Mem); |
| _mov(Var, TVar); |
| } |
| } else if (auto *RetVec = llvm::dyn_cast<VariableVecOn32>(ReturnReg)) { |
| auto *DestVecOn32 = llvm::cast<VariableVecOn32>(Dest); |
| for (SizeT i = 0; i < DestVecOn32->ContainersPerVector; ++i) { |
| _mov(DestVecOn32->getContainers()[i], RetVec->getContainers()[i]); |
| } |
| } else if (ReturnRegHi) { |
| assert(Dest->getType() == IceType_i64); |
| auto *Dest64On32 = llvm::cast<Variable64On32>(Dest); |
| Variable *DestLo = Dest64On32->getLo(); |
| Variable *DestHi = Dest64On32->getHi(); |
| _mov(DestLo, ReturnReg); |
| _mov(DestHi, ReturnRegHi); |
| } else { |
| assert(Dest->getType() == IceType_i32 || Dest->getType() == IceType_i16 || |
| Dest->getType() == IceType_i8 || Dest->getType() == IceType_i1 || |
| isScalarFloatingType(Dest->getType()) || |
| isVectorType(Dest->getType())); |
| _mov(Dest, ReturnReg); |
| } |
| } |
| } |
| |
| void TargetMIPS32::lowerCast(const InstCast *Instr) { |
| InstCast::OpKind CastKind = Instr->getCastKind(); |
| Variable *Dest = Instr->getDest(); |
| Operand *Src0 = legalizeUndef(Instr->getSrc(0)); |
| const Type DestTy = Dest->getType(); |
| const Type Src0Ty = Src0->getType(); |
| const uint32_t ShiftAmount = |
| (Src0Ty == IceType_i1 |
| ? INT32_BITS - 1 |
| : INT32_BITS - (CHAR_BITS * typeWidthInBytes(Src0Ty))); |
| const uint32_t Mask = |
| (Src0Ty == IceType_i1 |
| ? 1 |
| : (1 << (CHAR_BITS * typeWidthInBytes(Src0Ty))) - 1); |
| |
| if (isVectorType(DestTy)) { |
| llvm::report_fatal_error("Cast: Destination type is vector"); |
| return; |
| } |
| switch (CastKind) { |
| default: |
| Func->setError("Cast type not supported"); |
| return; |
| case InstCast::Sext: { |
| if (DestTy == IceType_i64) { |
| auto *DestLo = llvm::cast<Variable>(loOperand(Dest)); |
| auto *DestHi = llvm::cast<Variable>(hiOperand(Dest)); |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *T1_Lo = I32Reg(); |
| Variable *T2_Lo = I32Reg(); |
| Variable *T_Hi = I32Reg(); |
| if (Src0Ty == IceType_i1) { |
| _sll(T1_Lo, Src0R, INT32_BITS - 1); |
| _sra(T2_Lo, T1_Lo, INT32_BITS - 1); |
| _mov(DestHi, T2_Lo); |
| _mov(DestLo, T2_Lo); |
| } else if (Src0Ty == IceType_i8 || Src0Ty == IceType_i16) { |
| _sll(T1_Lo, Src0R, ShiftAmount); |
| _sra(T2_Lo, T1_Lo, ShiftAmount); |
| _sra(T_Hi, T2_Lo, INT32_BITS - 1); |
| _mov(DestHi, T_Hi); |
| _mov(DestLo, T2_Lo); |
| } else if (Src0Ty == IceType_i32) { |
| _mov(T1_Lo, Src0R); |
| _sra(T_Hi, T1_Lo, INT32_BITS - 1); |
| _mov(DestHi, T_Hi); |
| _mov(DestLo, T1_Lo); |
| } |
| } else { |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *T1 = makeReg(DestTy); |
| Variable *T2 = makeReg(DestTy); |
| if (Src0Ty == IceType_i1 || Src0Ty == IceType_i8 || |
| Src0Ty == IceType_i16) { |
| _sll(T1, Src0R, ShiftAmount); |
| _sra(T2, T1, ShiftAmount); |
| _mov(Dest, T2); |
| } |
| } |
| break; |
| } |
| case InstCast::Zext: { |
| if (DestTy == IceType_i64) { |
| auto *DestLo = llvm::cast<Variable>(loOperand(Dest)); |
| auto *DestHi = llvm::cast<Variable>(hiOperand(Dest)); |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *T_Lo = I32Reg(); |
| Variable *T_Hi = I32Reg(); |
| |
| if (Src0Ty == IceType_i1 || Src0Ty == IceType_i8 || Src0Ty == IceType_i16) |
| _andi(T_Lo, Src0R, Mask); |
| else if (Src0Ty == IceType_i32) |
| _mov(T_Lo, Src0R); |
| else |
| assert(Src0Ty != IceType_i64); |
| _mov(DestLo, T_Lo); |
| |
| auto *Zero = getZero(); |
| _addiu(T_Hi, Zero, 0); |
| _mov(DestHi, T_Hi); |
| } else { |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *T = makeReg(DestTy); |
| if (Src0Ty == IceType_i1 || Src0Ty == IceType_i8 || |
| Src0Ty == IceType_i16) { |
| _andi(T, Src0R, Mask); |
| _mov(Dest, T); |
| } |
| } |
| break; |
| } |
| case InstCast::Trunc: { |
| if (Src0Ty == IceType_i64) |
| Src0 = loOperand(Src0); |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *T = makeReg(DestTy); |
| switch (DestTy) { |
| case IceType_i1: |
| _andi(T, Src0R, 0x1); |
| break; |
| case IceType_i8: |
| _andi(T, Src0R, 0xff); |
| break; |
| case IceType_i16: |
| _andi(T, Src0R, 0xffff); |
| break; |
| default: |
| _mov(T, Src0R); |
| break; |
| } |
| _mov(Dest, T); |
| break; |
| } |
| case InstCast::Fptrunc: { |
| assert(Dest->getType() == IceType_f32); |
| assert(Src0->getType() == IceType_f64); |
| auto *DestR = legalizeToReg(Dest); |
| auto *Src0R = legalizeToReg(Src0); |
| _cvt_s_d(DestR, Src0R); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstCast::Fpext: { |
| assert(Dest->getType() == IceType_f64); |
| assert(Src0->getType() == IceType_f32); |
| auto *DestR = legalizeToReg(Dest); |
| auto *Src0R = legalizeToReg(Src0); |
| _cvt_d_s(DestR, Src0R); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstCast::Fptosi: |
| case InstCast::Fptoui: { |
| if (llvm::isa<Variable64On32>(Dest)) { |
| llvm::report_fatal_error("fp-to-i64 should have been prelowered."); |
| return; |
| } |
| if (DestTy != IceType_i64) { |
| if (Src0Ty == IceType_f32 && isScalarIntegerType(DestTy)) { |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *FTmp = makeReg(IceType_f32); |
| _trunc_w_s(FTmp, Src0R); |
| _mov(Dest, FTmp); |
| return; |
| } |
| if (Src0Ty == IceType_f64 && isScalarIntegerType(DestTy)) { |
| Variable *Src0R = legalizeToReg(Src0); |
| Variable *FTmp = makeReg(IceType_f64); |
| _trunc_w_d(FTmp, Src0R); |
| _mov(Dest, FTmp); |
| return; |
| } |
| } |
| llvm::report_fatal_error("Destination is i64 in fp-to-i32"); |
| break; |
| } |
| case InstCast::Sitofp: |
| case InstCast::Uitofp: { |
| if (llvm::isa<Variable64On32>(Dest)) { |
| llvm::report_fatal_error("i64-to-fp should have been prelowered."); |
| return; |
| } |
| if (Src0Ty != IceType_i64) { |
| Variable *Src0R = legalizeToReg(Src0); |
| auto *T0R = Src0R; |
| if (Src0Ty != IceType_i32) { |
| T0R = makeReg(IceType_i32); |
| if (CastKind == InstCast::Uitofp) |
| lowerCast(InstCast::create(Func, InstCast::Zext, T0R, Src0R)); |
| else |
| lowerCast(InstCast::create(Func, InstCast::Sext, T0R, Src0R)); |
| } |
| if (isScalarIntegerType(Src0Ty) && DestTy == IceType_f32) { |
| Variable *FTmp1 = makeReg(IceType_f32); |
| Variable *FTmp2 = makeReg(IceType_f32); |
| _mtc1(FTmp1, T0R); |
| _cvt_s_w(FTmp2, FTmp1); |
| _mov(Dest, FTmp2); |
| return; |
| } |
| if (isScalarIntegerType(Src0Ty) && DestTy == IceType_f64) { |
| Variable *FTmp1 = makeReg(IceType_f64); |
| Variable *FTmp2 = makeReg(IceType_f64); |
| _mtc1(FTmp1, T0R); |
| _cvt_d_w(FTmp2, FTmp1); |
| _mov(Dest, FTmp2); |
| return; |
| } |
| } |
| llvm::report_fatal_error("Source is i64 in i32-to-fp"); |
| break; |
| } |
| case InstCast::Bitcast: { |
| Operand *Src0 = Instr->getSrc(0); |
| if (DestTy == Src0->getType()) { |
| auto *Assign = InstAssign::create(Func, Dest, Src0); |
| lowerAssign(Assign); |
| return; |
| } |
| if (isVectorType(DestTy) || isVectorType(Src0->getType())) { |
| llvm::report_fatal_error( |
| "Bitcast: vector type should have been prelowered."); |
| return; |
| } |
| switch (DestTy) { |
| case IceType_NUM: |
| case IceType_void: |
| llvm::report_fatal_error("Unexpected bitcast."); |
| case IceType_i1: |
| UnimplementedLoweringError(this, Instr); |
| break; |
| case IceType_i8: |
| assert(Src0->getType() == IceType_v8i1); |
| llvm::report_fatal_error( |
| "i8 to v8i1 conversion should have been prelowered."); |
| break; |
| case IceType_i16: |
| assert(Src0->getType() == IceType_v16i1); |
| llvm::report_fatal_error( |
| "i16 to v16i1 conversion should have been prelowered."); |
| break; |
| case IceType_i32: |
| case IceType_f32: { |
| Variable *Src0R = legalizeToReg(Src0); |
| _mov(Dest, Src0R); |
| break; |
| } |
| case IceType_i64: { |
| assert(Src0->getType() == IceType_f64); |
| Variable *Src0R = legalizeToReg(Src0); |
| auto *T = llvm::cast<Variable64On32>(Func->makeVariable(IceType_i64)); |
| T->initHiLo(Func); |
| T->getHi()->setMustNotHaveReg(); |
| T->getLo()->setMustNotHaveReg(); |
| Context.insert<InstFakeDef>(T->getHi()); |
| Context.insert<InstFakeDef>(T->getLo()); |
| _mov_fp64_to_i64(T->getHi(), Src0R, Int64_Hi); |
| _mov_fp64_to_i64(T->getLo(), Src0R, Int64_Lo); |
| lowerAssign(InstAssign::create(Func, Dest, T)); |
| break; |
| } |
| case IceType_f64: { |
| assert(Src0->getType() == IceType_i64); |
| const uint32_t Mask = 0xFFFFFFFF; |
| if (auto *C64 = llvm::dyn_cast<ConstantInteger64>(Src0)) { |
| Variable *RegHi, *RegLo; |
| const uint64_t Value = C64->getValue(); |
| uint64_t Upper32Bits = (Value >> INT32_BITS) & Mask; |
| uint64_t Lower32Bits = Value & Mask; |
| RegLo = legalizeToReg(Ctx->getConstantInt32(Lower32Bits)); |
| RegHi = legalizeToReg(Ctx->getConstantInt32(Upper32Bits)); |
| _mov(Dest, RegHi, RegLo); |
| } else { |
| auto *Var64On32 = llvm::cast<Variable64On32>(Src0); |
| auto *RegLo = legalizeToReg(loOperand(Var64On32)); |
| auto *RegHi = legalizeToReg(hiOperand(Var64On32)); |
| _mov(Dest, RegHi, RegLo); |
| } |
| break; |
| } |
| default: |
| llvm::report_fatal_error("Unexpected bitcast."); |
| } |
| break; |
| } |
| } |
| } |
| |
| void TargetMIPS32::lowerExtractElement(const InstExtractElement *Instr) { |
| Variable *Dest = Instr->getDest(); |
| const Type DestTy = Dest->getType(); |
| Operand *Src1 = Instr->getSrc(1); |
| if (const auto *Imm = llvm::dyn_cast<ConstantInteger32>(Src1)) { |
| const uint32_t Index = Imm->getValue(); |
| Variable *TDest = makeReg(DestTy); |
| Variable *TReg = makeReg(DestTy); |
| auto *Src0 = legalizeUndef(Instr->getSrc(0)); |
| auto *Src0R = llvm::dyn_cast<VariableVecOn32>(Src0); |
| // Number of elements in each container |
| uint32_t ElemPerCont = |
| typeNumElements(Src0->getType()) / Src0R->ContainersPerVector; |
| auto *Src = Src0R->getContainers()[Index / ElemPerCont]; |
| auto *SrcE = legalizeToReg(Src); |
| // Position of the element in the container |
| uint32_t PosInCont = Index % ElemPerCont; |
| if (ElemPerCont == 1) { |
| _mov(TDest, SrcE); |
| } else if (ElemPerCont == 2) { |
| switch (PosInCont) { |
| case 0: |
| _andi(TDest, SrcE, 0xffff); |
| break; |
| case 1: |
| _srl(TDest, SrcE, 16); |
| break; |
| default: |
| llvm::report_fatal_error("ExtractElement: Invalid PosInCont"); |
| break; |
| } |
| } else if (ElemPerCont == 4) { |
| switch (PosInCont) { |
| case 0: |
| _andi(TDest, SrcE, 0xff); |
| break; |
| case 1: |
| _srl(TReg, SrcE, 8); |
| _andi(TDest, TReg, 0xff); |
| break; |
| case 2: |
| _srl(TReg, SrcE, 16); |
| _andi(TDest, TReg, 0xff); |
| break; |
| case 3: |
| _srl(TDest, SrcE, 24); |
| break; |
| default: |
| llvm::report_fatal_error("ExtractElement: Invalid PosInCont"); |
| break; |
| } |
| } |
| if (typeElementType(Src0R->getType()) == IceType_i1) { |
| Variable *TReg1 = makeReg(DestTy); |
| _andi(TReg1, TDest, 0x1); |
| _mov(Dest, TReg1); |
| } else { |
| _mov(Dest, TDest); |
| } |
| return; |
| } |
| llvm::report_fatal_error("ExtractElement requires a constant index"); |
| } |
| |
| void TargetMIPS32::lowerFcmp(const InstFcmp *Instr) { |
| Variable *Dest = Instr->getDest(); |
| if (isVectorType(Dest->getType())) { |
| llvm::report_fatal_error("Fcmp: Destination type is vector"); |
| return; |
| } |
| |
| auto *Src0 = Instr->getSrc(0); |
| auto *Src1 = Instr->getSrc(1); |
| auto *Zero = getZero(); |
| |
| InstFcmp::FCond Cond = Instr->getCondition(); |
| auto *DestR = makeReg(IceType_i32); |
| auto *Src0R = legalizeToReg(Src0); |
| auto *Src1R = legalizeToReg(Src1); |
| const Type Src0Ty = Src0->getType(); |
| |
| Operand *FCC0 = OperandMIPS32FCC::create(getFunc(), OperandMIPS32FCC::FCC0); |
| |
| switch (Cond) { |
| default: { |
| llvm::report_fatal_error("Unhandled fp comparison."); |
| return; |
| } |
| case InstFcmp::False: { |
| Context.insert<InstFakeUse>(Src0R); |
| Context.insert<InstFakeUse>(Src1R); |
| _addiu(DestR, Zero, 0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Oeq: { |
| if (Src0Ty == IceType_f32) { |
| _c_eq_s(Src0R, Src1R); |
| } else { |
| _c_eq_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movf(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Ogt: { |
| if (Src0Ty == IceType_f32) { |
| _c_ule_s(Src0R, Src1R); |
| } else { |
| _c_ule_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movt(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Oge: { |
| if (Src0Ty == IceType_f32) { |
| _c_ult_s(Src0R, Src1R); |
| } else { |
| _c_ult_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movt(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Olt: { |
| if (Src0Ty == IceType_f32) { |
| _c_olt_s(Src0R, Src1R); |
| } else { |
| _c_olt_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movf(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Ole: { |
| if (Src0Ty == IceType_f32) { |
| _c_ole_s(Src0R, Src1R); |
| } else { |
| _c_ole_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movf(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::One: { |
| if (Src0Ty == IceType_f32) { |
| _c_ueq_s(Src0R, Src1R); |
| } else { |
| _c_ueq_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movt(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Ord: { |
| if (Src0Ty == IceType_f32) { |
| _c_un_s(Src0R, Src1R); |
| } else { |
| _c_un_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movt(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Ueq: { |
| if (Src0Ty == IceType_f32) { |
| _c_ueq_s(Src0R, Src1R); |
| } else { |
| _c_ueq_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movf(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Ugt: { |
| if (Src0Ty == IceType_f32) { |
| _c_ole_s(Src0R, Src1R); |
| } else { |
| _c_ole_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movt(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Uge: { |
| if (Src0Ty == IceType_f32) { |
| _c_olt_s(Src0R, Src1R); |
| } else { |
| _c_olt_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movt(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Ult: { |
| if (Src0Ty == IceType_f32) { |
| _c_ult_s(Src0R, Src1R); |
| } else { |
| _c_ult_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movf(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Ule: { |
| if (Src0Ty == IceType_f32) { |
| _c_ule_s(Src0R, Src1R); |
| } else { |
| _c_ule_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movf(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Une: { |
| if (Src0Ty == IceType_f32) { |
| _c_eq_s(Src0R, Src1R); |
| } else { |
| _c_eq_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movt(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::Uno: { |
| if (Src0Ty == IceType_f32) { |
| _c_un_s(Src0R, Src1R); |
| } else { |
| _c_un_d(Src0R, Src1R); |
| } |
| _addiu(DestR, Zero, 1); |
| _movf(DestR, Zero, FCC0); |
| _mov(Dest, DestR); |
| break; |
| } |
| case InstFcmp::True: { |
| Context.insert<InstFakeUse>(Src0R); |
| Context.insert<InstFakeUse>(Src1R); |
| _addiu(DestR, Zero, 1); |
| _mov(Dest, DestR); |
| break; |
| } |
| } |
| } |
| |
| void TargetMIPS32::lower64Icmp(const InstIcmp *Instr) { |
| Operand *Src0 = legalize(Instr->getSrc(0)); |
| Operand *Src1 = legalize(Instr->getSrc(1)); |
| Variable *Dest = Instr->getDest(); |
| InstIcmp::ICond Condition = Instr->getCondition(); |
| |
| Variable *Src0LoR = legalizeToReg(loOperand(Src0)); |
| Variable *Src0HiR = legalizeToReg(hiOperand(Src0)); |
| Variable *Src1LoR = legalizeToReg(loOperand(Src1)); |
| Variable *Src1HiR = legalizeToReg(hiOperand(Src1)); |
| |
| switch (Condition) { |
| default: |
| llvm_unreachable("unexpected condition"); |
| return; |
| case InstIcmp::Eq: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _xor(T2, Src0LoR, Src1LoR); |
| _or(T3, T1, T2); |
| _sltiu(T4, T3, 1); |
| _mov(Dest, T4); |
| return; |
| } |
| case InstIcmp::Ne: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _xor(T2, Src0LoR, Src1LoR); |
| _or(T3, T1, T2); |
| _sltu(T4, getZero(), T3); |
| _mov(Dest, T4); |
| return; |
| } |
| case InstIcmp::Sgt: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _slt(T2, Src1HiR, Src0HiR); |
| _sltu(T3, Src1LoR, Src0LoR); |
| _movz(T2, T3, T1); |
| _mov(Dest, T2); |
| return; |
| } |
| case InstIcmp::Ugt: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _sltu(T2, Src1HiR, Src0HiR); |
| _sltu(T3, Src1LoR, Src0LoR); |
| _movz(T2, T3, T1); |
| _mov(Dest, T2); |
| return; |
| } |
| case InstIcmp::Sge: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _slt(T2, Src0HiR, Src1HiR); |
| _xori(T3, T2, 1); |
| _sltu(T4, Src0LoR, Src1LoR); |
| _xori(T5, T4, 1); |
| _movz(T3, T5, T1); |
| _mov(Dest, T3); |
| return; |
| } |
| case InstIcmp::Uge: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _sltu(T2, Src0HiR, Src1HiR); |
| _xori(T3, T2, 1); |
| _sltu(T4, Src0LoR, Src1LoR); |
| _xori(T5, T4, 1); |
| _movz(T3, T5, T1); |
| _mov(Dest, T3); |
| return; |
| } |
| case InstIcmp::Slt: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _slt(T2, Src0HiR, Src1HiR); |
| _sltu(T3, Src0LoR, Src1LoR); |
| _movz(T2, T3, T1); |
| _mov(Dest, T2); |
| return; |
| } |
| case InstIcmp::Ult: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _sltu(T2, Src0HiR, Src1HiR); |
| _sltu(T3, Src0LoR, Src1LoR); |
| _movz(T2, T3, T1); |
| _mov(Dest, T2); |
| return; |
| } |
| case InstIcmp::Sle: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _slt(T2, Src1HiR, Src0HiR); |
| _xori(T3, T2, 1); |
| _sltu(T4, Src1LoR, Src0LoR); |
| _xori(T5, T4, 1); |
| _movz(T3, T5, T1); |
| _mov(Dest, T3); |
| return; |
| } |
| case InstIcmp::Ule: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| _xor(T1, Src0HiR, Src1HiR); |
| _sltu(T2, Src1HiR, Src0HiR); |
| _xori(T3, T2, 1); |
| _sltu(T4, Src1LoR, Src0LoR); |
| _xori(T5, T4, 1); |
| _movz(T3, T5, T1); |
| _mov(Dest, T3); |
| return; |
| } |
| } |
| } |
| |
| void TargetMIPS32::lowerIcmp(const InstIcmp *Instr) { |
| auto *Src0 = Instr->getSrc(0); |
| auto *Src1 = Instr->getSrc(1); |
| if (Src0->getType() == IceType_i64) { |
| lower64Icmp(Instr); |
| return; |
| } |
| Variable *Dest = Instr->getDest(); |
| if (isVectorType(Dest->getType())) { |
| llvm::report_fatal_error("Icmp: Destination type is vector"); |
| return; |
| } |
| InstIcmp::ICond Cond = Instr->getCondition(); |
| auto *Src0R = legalizeToReg(Src0); |
| auto *Src1R = legalizeToReg(Src1); |
| const Type Src0Ty = Src0R->getType(); |
| const uint32_t ShAmt = INT32_BITS - getScalarIntBitWidth(Src0->getType()); |
| Variable *Src0RT = I32Reg(); |
| Variable *Src1RT = I32Reg(); |
| |
| if (Src0Ty != IceType_i32) { |
| _sll(Src0RT, Src0R, ShAmt); |
| _sll(Src1RT, Src1R, ShAmt); |
| } else { |
| _mov(Src0RT, Src0R); |
| _mov(Src1RT, Src1R); |
| } |
| |
| switch (Cond) { |
| case InstIcmp::Eq: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| _xor(T, Src0RT, Src1RT); |
| _sltiu(DestT, T, 1); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Ne: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| auto *Zero = getZero(); |
| _xor(T, Src0RT, Src1RT); |
| _sltu(DestT, Zero, T); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Ugt: { |
| auto *DestT = I32Reg(); |
| _sltu(DestT, Src1RT, Src0RT); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Uge: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| _sltu(T, Src0RT, Src1RT); |
| _xori(DestT, T, 1); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Ult: { |
| auto *DestT = I32Reg(); |
| _sltu(DestT, Src0RT, Src1RT); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Ule: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| _sltu(T, Src1RT, Src0RT); |
| _xori(DestT, T, 1); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Sgt: { |
| auto *DestT = I32Reg(); |
| _slt(DestT, Src1RT, Src0RT); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Sge: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| _slt(T, Src0RT, Src1RT); |
| _xori(DestT, T, 1); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Slt: { |
| auto *DestT = I32Reg(); |
| _slt(DestT, Src0RT, Src1RT); |
| _mov(Dest, DestT); |
| return; |
| } |
| case InstIcmp::Sle: { |
| auto *DestT = I32Reg(); |
| auto *T = I32Reg(); |
| _slt(T, Src1RT, Src0RT); |
| _xori(DestT, T, 1); |
| _mov(Dest, DestT); |
| return; |
| } |
| default: |
| llvm_unreachable("Invalid ICmp operator"); |
| return; |
| } |
| } |
| |
| void TargetMIPS32::lowerInsertElement(const InstInsertElement *Instr) { |
| Variable *Dest = Instr->getDest(); |
| const Type DestTy = Dest->getType(); |
| Operand *Src2 = Instr->getSrc(2); |
| if (const auto *Imm = llvm::dyn_cast<ConstantInteger32>(Src2)) { |
| const uint32_t Index = Imm->getValue(); |
| // Vector to insert in |
| auto *Src0 = legalizeUndef(Instr->getSrc(0)); |
| auto *Src0R = llvm::dyn_cast<VariableVecOn32>(Src0); |
| // Number of elements in each container |
| uint32_t ElemPerCont = |
| typeNumElements(Src0->getType()) / Src0R->ContainersPerVector; |
| // Source Element |
| auto *Src = Src0R->getContainers()[Index / ElemPerCont]; |
| auto *SrcE = Src; |
| if (ElemPerCont > 1) |
| SrcE = legalizeToReg(Src); |
| // Dest is a vector |
| auto *VDest = llvm::dyn_cast<VariableVecOn32>(Dest); |
| VDest->initVecElement(Func); |
| // Temp vector variable |
| auto *TDest = makeReg(DestTy); |
| auto *TVDest = llvm::dyn_cast<VariableVecOn32>(TDest); |
| TVDest->initVecElement(Func); |
| // Destination element |
| auto *DstE = TVDest->getContainers()[Index / ElemPerCont]; |
| // Element to insert |
| auto *Src1R = legalizeToReg(Instr->getSrc(1)); |
| auto *TReg1 = makeReg(IceType_i32); |
| auto *TReg2 = makeReg(IceType_i32); |
| auto *TReg3 = makeReg(IceType_i32); |
| auto *TReg4 = makeReg(IceType_i32); |
| auto *TReg5 = makeReg(IceType_i32); |
| auto *TDReg = makeReg(IceType_i32); |
| // Position of the element in the container |
| uint32_t PosInCont = Index % ElemPerCont; |
| // Load source vector in a temporary vector |
| for (SizeT i = 0; i < TVDest->ContainersPerVector; ++i) { |
| auto *DCont = TVDest->getContainers()[i]; |
| // Do not define DstE as we are going to redefine it |
| if (DCont == DstE) |
| continue; |
| auto *SCont = Src0R->getContainers()[i]; |
| auto *TReg = makeReg(IceType_i32); |
| _mov(TReg, SCont); |
| _mov(DCont, TReg); |
| } |
| // Insert the element |
| if (ElemPerCont == 1) { |
| _mov(DstE, Src1R); |
| } else if (ElemPerCont == 2) { |
| switch (PosInCont) { |
| case 0: |
| _andi(TReg1, Src1R, 0xffff); // Clear upper 16-bits of source |
| _srl(TReg2, SrcE, 16); |
| _sll(TReg3, TReg2, 16); // Clear lower 16-bits of element |
| _or(TDReg, TReg1, TReg3); |
| _mov(DstE, TDReg); |
| break; |
| case 1: |
| _sll(TReg1, Src1R, 16); // Clear lower 16-bits of source |
| _sll(TReg2, SrcE, 16); |
| _srl(TReg3, TReg2, 16); // Clear upper 16-bits of element |
| _or(TDReg, TReg1, TReg3); |
| _mov(DstE, TDReg); |
| break; |
| default: |
| llvm::report_fatal_error("InsertElement: Invalid PosInCont"); |
| break; |
| } |
| } else if (ElemPerCont == 4) { |
| switch (PosInCont) { |
| case 0: |
| _andi(TReg1, Src1R, 0xff); // Clear bits[31:8] of source |
| _srl(TReg2, SrcE, 8); |
| _sll(TReg3, TReg2, 8); // Clear bits[7:0] of element |
| _or(TDReg, TReg1, TReg3); |
| _mov(DstE, TDReg); |
| break; |
| case 1: |
| _andi(TReg1, Src1R, 0xff); // Clear bits[31:8] of source |
| _sll(TReg5, TReg1, 8); // Position in the destination |
| _lui(TReg2, Ctx->getConstantInt32(0xffff)); |
| _ori(TReg3, TReg2, 0x00ff); |
| _and(TReg4, SrcE, TReg3); // Clear bits[15:8] of element |
| _or(TDReg, TReg5, TReg4); |
| _mov(DstE, TDReg); |
| break; |
| case 2: |
| _andi(TReg1, Src1R, 0xff); // Clear bits[31:8] of source |
| _sll(TReg5, TReg1, 16); // Position in the destination |
| _lui(TReg2, Ctx->getConstantInt32(0xff00)); |
| _ori(TReg3, TReg2, 0xffff); |
| _and(TReg4, SrcE, TReg3); // Clear bits[15:8] of element |
| _or(TDReg, TReg5, TReg4); |
| _mov(DstE, TDReg); |
| break; |
| case 3: |
| _sll(TReg1, Src1R, 24); // Position in the destination |
| _sll(TReg2, SrcE, 8); |
| _srl(TReg3, TReg2, 8); // Clear bits[31:24] of element |
| _or(TDReg, TReg1, TReg3); |
| _mov(DstE, TDReg); |
| break; |
| default: |
| llvm::report_fatal_error("InsertElement: Invalid PosInCont"); |
| break; |
| } |
| } |
| // Write back temporary vector to the destination |
| auto *Assign = InstAssign::create(Func, Dest, TDest); |
| lowerAssign(Assign); |
| return; |
| } |
| llvm::report_fatal_error("InsertElement requires a constant index"); |
| } |
| |
| void TargetMIPS32::createArithInst(Intrinsics::AtomicRMWOperation Operation, |
| Variable *Dest, Variable *Src0, |
| Variable *Src1) { |
| switch (Operation) { |
| default: |
| llvm::report_fatal_error("Unknown AtomicRMW operation"); |
| case Intrinsics::AtomicExchange: |
| llvm::report_fatal_error("Can't handle Atomic xchg operation"); |
| case Intrinsics::AtomicAdd: |
| _addu(Dest, Src0, Src1); |
| break; |
| case Intrinsics::AtomicAnd: |
| _and(Dest, Src0, Src1); |
| break; |
| case Intrinsics::AtomicSub: |
| _subu(Dest, Src0, Src1); |
| break; |
| case Intrinsics::AtomicOr: |
| _or(Dest, Src0, Src1); |
| break; |
| case Intrinsics::AtomicXor: |
| _xor(Dest, Src0, Src1); |
| break; |
| } |
| } |
| |
| void TargetMIPS32::lowerIntrinsicCall(const InstIntrinsicCall *Instr) { |
| Variable *Dest = Instr->getDest(); |
| Type DestTy = (Dest == nullptr) ? IceType_void : Dest->getType(); |
| |
| Intrinsics::IntrinsicID ID = Instr->getIntrinsicInfo().ID; |
| switch (ID) { |
| case Intrinsics::AtomicLoad: { |
| assert(isScalarIntegerType(DestTy)); |
| // We require the memory address to be naturally aligned. Given that is the |
| // case, then normal loads are atomic. |
| if (!Intrinsics::isMemoryOrderValid( |
| ID, getConstantMemoryOrder(Instr->getArg(1)))) { |
| Func->setError("Unexpected memory ordering for AtomicLoad"); |
| return; |
| } |
| if (DestTy == IceType_i64) { |
| llvm::report_fatal_error("AtomicLoad.i64 should have been prelowered."); |
| return; |
| } else if (DestTy == IceType_i32) { |
| auto *T1 = makeReg(DestTy); |
| auto *RegAt = getPhysicalRegister(RegMIPS32::Reg_AT); |
| auto *Base = legalizeToReg(Instr->getArg(0)); |
| auto *Addr = formMemoryOperand(Base, DestTy); |
| InstMIPS32Label *Retry = InstMIPS32Label::create(Func, this); |
| InstMIPS32Label *Exit = InstMIPS32Label::create(Func, this); |
| constexpr CfgNode *NoTarget = nullptr; |
| _sync(); |
| Context.insert(Retry); |
| Sandboxer(this).ll(T1, Addr); |
| _br(NoTarget, NoTarget, T1, getZero(), Exit, CondMIPS32::Cond::NE); |
| _addiu(RegAt, getZero(), 0); // Loaded value is zero here, writeback zero |
| Sandboxer(this).sc(RegAt, Addr); |
| _br(NoTarget, NoTarget, RegAt, getZero(), Retry, CondMIPS32::Cond::EQ); |
| Context.insert(Exit); |
| _sync(); |
| _mov(Dest, T1); |
| Context.insert<InstFakeUse>(T1); |
| } else { |
| const uint32_t Mask = (1 << (CHAR_BITS * typeWidthInBytes(DestTy))) - 1; |
| auto *Base = legalizeToReg(Instr->getArg(0)); |
| auto *T1 = makeReg(IceType_i32); |
| auto *T2 = makeReg(IceType_i32); |
| auto *T3 = makeReg(IceType_i32); |
| auto *T4 = makeReg(IceType_i32); |
| auto *T5 = makeReg(IceType_i32); |
| auto *T6 = makeReg(IceType_i32); |
| auto *SrcMask = makeReg(IceType_i32); |
| auto *Tdest = makeReg(IceType_i32); |
| auto *RegAt = getPhysicalRegister(RegMIPS32::Reg_AT); |
| InstMIPS32Label *Retry = InstMIPS32Label::create(Func, this); |
| InstMIPS32Label *Exit = InstMIPS32Label::create(Func, this); |
| constexpr CfgNode *NoTarget = nullptr; |
| _sync(); |
| _addiu(T1, getZero(), -4); // Address mask 0xFFFFFFFC |
| _andi(T2, Base, 3); // Last two bits of the address |
| _and(T3, Base, T1); // Align the address |
| _sll(T4, T2, 3); |
| _ori(T5, getZero(), Mask); |
| _sllv(SrcMask, T5, T4); // Source mask |
| auto *Addr = formMemoryOperand(T3, IceType_i32); |
| Context.insert(Retry); |
| Sandboxer(this).ll(T6, Addr); |
| _and(Tdest, T6, SrcMask); |
| _br(NoTarget, NoTarget, T6, getZero(), Exit, CondMIPS32::Cond::NE); |
| _addiu(RegAt, getZero(), 0); // Loaded value is zero here, writeback zero |
| Sandboxer(this).sc(RegAt, Addr); |
| _br(NoTarget, NoTarget, RegAt, getZero(), Retry, CondMIPS32::Cond::EQ); |
| Context.insert(Exit); |
| auto *T7 = makeReg(IceType_i32); |
| auto *T8 = makeReg(IceType_i32); |
| _srlv(T7, Tdest, T4); |
| _andi(T8, T7, Mask); |
| _sync(); |
| _mov(Dest, T8); |
| Context.insert<InstFakeUse>(T6); |
| Context.insert<InstFakeUse>(SrcMask); |
| } |
| return; |
| } |
| case Intrinsics::AtomicStore: { |
| // We require the memory address to be naturally aligned. Given that is the |
| // case, then normal stores are atomic. |
| if (!Intrinsics::isMemoryOrderValid( |
| ID, getConstantMemoryOrder(Instr->getArg(2)))) { |
| Func->setError("Unexpected memory ordering for AtomicStore"); |
| return; |
| } |
| auto *Val = Instr->getArg(0); |
| auto Ty = Val->getType(); |
| if (Ty == IceType_i64) { |
| llvm::report_fatal_error("AtomicStore.i64 should have been prelowered."); |
| return; |
| } else if (Ty == IceType_i32) { |
| auto *Val = legalizeToReg(Instr->getArg(0)); |
| auto *Base = legalizeToReg(Instr->getArg(1)); |
| auto *Addr = formMemoryOperand(Base, Ty); |
| InstMIPS32Label *Retry = InstMIPS32Label::create(Func, this); |
| constexpr CfgNode *NoTarget = nullptr; |
| auto *T1 = makeReg(IceType_i32); |
| auto *RegAt = getPhysicalRegister(RegMIPS32::Reg_AT); |
| _sync(); |
| Context.insert(Retry); |
| Sandboxer(this).ll(T1, Addr); |
| _mov(RegAt, Val); |
| Sandboxer(this).sc(RegAt, Addr); |
| _br(NoTarget, NoTarget, RegAt, getZero(), Retry, CondMIPS32::Cond::EQ); |
| Context.insert<InstFakeUse>(T1); // To keep LL alive |
| _sync(); |
| } else { |
| auto *Val = legalizeToReg(Instr->getArg(0)); |
| auto *Base = legalizeToReg(Instr->getArg(1)); |
| InstMIPS32Label *Retry = InstMIPS32Label::create(Func, this); |
| constexpr CfgNode *NoTarget = nullptr; |
| auto *T1 = makeReg(IceType_i32); |
| auto *T2 = makeReg(IceType_i32); |
| auto *T3 = makeReg(IceType_i32); |
| auto *T4 = makeReg(IceType_i32); |
| auto *T5 = makeReg(IceType_i32); |
| auto *T6 = makeReg(IceType_i32); |
| auto *T7 = makeReg(IceType_i32); |
| auto *RegAt = getPhysicalRegister(RegMIPS32::Reg_AT); |
| auto *SrcMask = makeReg(IceType_i32); |
| auto *DstMask = makeReg(IceType_i32); |
| const uint32_t Mask = (1 << (CHAR_BITS * typeWidthInBytes(Ty))) - 1; |
| _sync(); |
| _addiu(T1, getZero(), -4); |
| _and(T7, Base, T1); |
| auto *Addr = formMemoryOperand(T7, Ty); |
| _andi(T2, Base, 3); |
| _sll(T3, T2, 3); |
| _ori(T4, getZero(), Mask); |
| _sllv(T5, T4, T3); |
| _sllv(T6, Val, T3); |
| _nor(SrcMask, getZero(), T5); |
| _and(DstMask, T6, T5); |
| Context.insert(Retry); |
| Sandboxer(this).ll(RegAt, Addr); |
| _and(RegAt, RegAt, SrcMask); |
| _or(RegAt, RegAt, DstMask); |
| Sandboxer(this).sc(RegAt, Addr); |
| _br(NoTarget, NoTarget, RegAt, getZero(), Retry, CondMIPS32::Cond::EQ); |
| Context.insert<InstFakeUse>(SrcMask); |
| Context.insert<InstFakeUse>(DstMask); |
| _sync(); |
| } |
| return; |
| } |
| case Intrinsics::AtomicCmpxchg: { |
| assert(isScalarIntegerType(DestTy)); |
| // We require the memory address to be naturally aligned. Given that is the |
| // case, then normal loads are atomic. |
| if (!Intrinsics::isMemoryOrderValid( |
| ID, getConstantMemoryOrder(Instr->getArg(3)), |
| getConstantMemoryOrder(Instr->getArg(4)))) { |
| Func->setError("Unexpected memory ordering for AtomicCmpxchg"); |
| return; |
| } |
| |
| InstMIPS32Label *Exit = InstMIPS32Label::create(Func, this); |
| InstMIPS32Label *Retry = InstMIPS32Label::create(Func, this); |
| constexpr CfgNode *NoTarget = nullptr; |
| auto *New = Instr->getArg(2); |
| auto *Expected = Instr->getArg(1); |
| auto *ActualAddress = Instr->getArg(0); |
| |
| if (DestTy == IceType_i64) { |
| llvm::report_fatal_error( |
| "AtomicCmpxchg.i64 should have been prelowered."); |
| return; |
| } else if (DestTy == IceType_i8 || DestTy == IceType_i16) { |
| auto *NewR = legalizeToReg(New); |
| auto *ExpectedR = legalizeToReg(Expected); |
| auto *ActualAddressR = legalizeToReg(ActualAddress); |
| const uint32_t ShiftAmount = |
| (INT32_BITS - CHAR_BITS * typeWidthInBytes(DestTy)); |
| const uint32_t Mask = (1 << (CHAR_BITS * typeWidthInBytes(DestTy))) - 1; |
| auto *RegAt = getPhysicalRegister(RegMIPS32::Reg_AT); |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| auto *T6 = I32Reg(); |
| auto *T7 = I32Reg(); |
| auto *T8 = I32Reg(); |
| auto *T9 = I32Reg(); |
| _addiu(RegAt, getZero(), -4); |
| _and(T1, ActualAddressR, RegAt); |
| auto *Addr = formMemoryOperand(T1, DestTy); |
| _andi(RegAt, ActualAddressR, 3); |
| _sll(T2, RegAt, 3); |
| _ori(RegAt, getZero(), Mask); |
| _sllv(T3, RegAt, T2); |
| _nor(T4, getZero(), T3); |
| _andi(RegAt, ExpectedR, Mask); |
| _sllv(T5, RegAt, T2); |
| _andi(RegAt, NewR, Mask); |
| _sllv(T6, RegAt, T2); |
| _sync(); |
| Context.insert(Retry); |
| Sandboxer(this).ll(T7, Addr); |
| _and(T8, T7, T3); |
| _br(NoTarget, NoTarget, T8, T5, Exit, CondMIPS32::Cond::NE); |
| _and(RegAt, T7, T4); |
| _or(T9, RegAt, T6); |
| Sandboxer(this).sc(T9, Addr); |
| _br(NoTarget, NoTarget, getZero(), T9, Retry, CondMIPS32::Cond::EQ); |
| Context.insert<InstFakeUse>(getZero()); |
| Context.insert(Exit); |
| _srlv(RegAt, T8, T2); |
| _sll(RegAt, RegAt, ShiftAmount); |
| _sra(RegAt, RegAt, ShiftAmount); |
| _mov(Dest, RegAt); |
| _sync(); |
| Context.insert<InstFakeUse>(T3); |
| Context.insert<InstFakeUse>(T4); |
| Context.insert<InstFakeUse>(T5); |
| Context.insert<InstFakeUse>(T6); |
| Context.insert<InstFakeUse>(T8); |
| Context.insert<InstFakeUse>(ExpectedR); |
| Context.insert<InstFakeUse>(NewR); |
| } else { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *NewR = legalizeToReg(New); |
| auto *ExpectedR = legalizeToReg(Expected); |
| auto *ActualAddressR = legalizeToReg(ActualAddress); |
| _sync(); |
| Context.insert(Retry); |
| Sandboxer(this).ll(T1, formMemoryOperand(ActualAddressR, DestTy)); |
| _br(NoTarget, NoTarget, T1, ExpectedR, Exit, CondMIPS32::Cond::NE); |
| _mov(T2, NewR); |
| Sandboxer(this).sc(T2, formMemoryOperand(ActualAddressR, DestTy)); |
| _br(NoTarget, NoTarget, T2, getZero(), Retry, CondMIPS32::Cond::EQ); |
| Context.insert<InstFakeUse>(getZero()); |
| Context.insert(Exit); |
| _mov(Dest, T1); |
| _sync(); |
| Context.insert<InstFakeUse>(ExpectedR); |
| Context.insert<InstFakeUse>(NewR); |
| } |
| return; |
| } |
| case Intrinsics::AtomicRMW: { |
| assert(isScalarIntegerType(DestTy)); |
| // We require the memory address to be naturally aligned. Given that is the |
| // case, then normal loads are atomic. |
| if (!Intrinsics::isMemoryOrderValid( |
| ID, getConstantMemoryOrder(Instr->getArg(3)))) { |
| Func->setError("Unexpected memory ordering for AtomicRMW"); |
| return; |
| } |
| |
| constexpr CfgNode *NoTarget = nullptr; |
| InstMIPS32Label *Retry = InstMIPS32Label::create(Func, this); |
| auto Operation = static_cast<Intrinsics::AtomicRMWOperation>( |
| llvm::cast<ConstantInteger32>(Instr->getArg(0))->getValue()); |
| auto *New = Instr->getArg(2); |
| auto *ActualAddress = Instr->getArg(1); |
| |
| if (DestTy == IceType_i64) { |
| llvm::report_fatal_error("AtomicRMW.i64 should have been prelowered."); |
| return; |
| } else if (DestTy == IceType_i8 || DestTy == IceType_i16) { |
| const uint32_t ShiftAmount = |
| INT32_BITS - (CHAR_BITS * typeWidthInBytes(DestTy)); |
| const uint32_t Mask = (1 << (CHAR_BITS * typeWidthInBytes(DestTy))) - 1; |
| auto *NewR = legalizeToReg(New); |
| auto *ActualAddressR = legalizeToReg(ActualAddress); |
| auto *RegAt = getPhysicalRegister(RegMIPS32::Reg_AT); |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| auto *T6 = I32Reg(); |
| auto *T7 = I32Reg(); |
| _sync(); |
| _addiu(RegAt, getZero(), -4); |
| _and(T1, ActualAddressR, RegAt); |
| _andi(RegAt, ActualAddressR, 3); |
| _sll(T2, RegAt, 3); |
| _ori(RegAt, getZero(), Mask); |
| _sllv(T3, RegAt, T2); |
| _nor(T4, getZero(), T3); |
| _sllv(T5, NewR, T2); |
| Context.insert(Retry); |
| Sandboxer(this).ll(T6, formMemoryOperand(T1, DestTy)); |
| if (Operation != Intrinsics::AtomicExchange) { |
| createArithInst(Operation, RegAt, T6, T5); |
| _and(RegAt, RegAt, T3); |
| } |
| _and(T7, T6, T4); |
| if (Operation == Intrinsics::AtomicExchange) { |
| _or(RegAt, T7, T5); |
| } else { |
| _or(RegAt, T7, RegAt); |
| } |
| Sandboxer(this).sc(RegAt, formMemoryOperand(T1, DestTy)); |
| _br(NoTarget, NoTarget, RegAt, getZero(), Retry, CondMIPS32::Cond::EQ); |
| Context.insert<InstFakeUse>(getZero()); |
| _and(RegAt, T6, T3); |
| _srlv(RegAt, RegAt, T2); |
| _sll(RegAt, RegAt, ShiftAmount); |
| _sra(RegAt, RegAt, ShiftAmount); |
| _mov(Dest, RegAt); |
| _sync(); |
| Context.insert<InstFakeUse>(NewR); |
| Context.insert<InstFakeUse>(Dest); |
| } else { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *NewR = legalizeToReg(New); |
| auto *ActualAddressR = legalizeToReg(ActualAddress); |
| _sync(); |
| Context.insert(Retry); |
| Sandboxer(this).ll(T1, formMemoryOperand(ActualAddressR, DestTy)); |
| if (Operation == Intrinsics::AtomicExchange) { |
| _mov(T2, NewR); |
| } else { |
| createArithInst(Operation, T2, T1, NewR); |
| } |
| Sandboxer(this).sc(T2, formMemoryOperand(ActualAddressR, DestTy)); |
| _br(NoTarget, NoTarget, T2, getZero(), Retry, CondMIPS32::Cond::EQ); |
| Context.insert<InstFakeUse>(getZero()); |
| _mov(Dest, T1); |
| _sync(); |
| Context.insert<InstFakeUse>(NewR); |
| Context.insert<InstFakeUse>(Dest); |
| } |
| return; |
| } |
| case Intrinsics::AtomicFence: |
| case Intrinsics::AtomicFenceAll: |
| assert(Dest == nullptr); |
| _sync(); |
| return; |
| case Intrinsics::AtomicIsLockFree: { |
| Operand *ByteSize = Instr->getArg(0); |
| auto *CI = llvm::dyn_cast<ConstantInteger32>(ByteSize); |
| auto *T = I32Reg(); |
| if (CI == nullptr) { |
| // The PNaCl ABI requires the byte size to be a compile-time constant. |
| Func->setError("AtomicIsLockFree byte size should be compile-time const"); |
| return; |
| } |
| static constexpr int32_t NotLockFree = 0; |
| static constexpr int32_t LockFree = 1; |
| int32_t Result = NotLockFree; |
| switch (CI->getValue()) { |
| case 1: |
| case 2: |
| case 4: |
| Result = LockFree; |
| break; |
| } |
| _addiu(T, getZero(), Result); |
| _mov(Dest, T); |
| return; |
| } |
| case Intrinsics::Bswap: { |
| auto *Src = Instr->getArg(0); |
| const Type SrcTy = Src->getType(); |
| assert(SrcTy == IceType_i16 || SrcTy == IceType_i32 || |
| SrcTy == IceType_i64); |
| switch (SrcTy) { |
| case IceType_i16: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *SrcR = legalizeToReg(Src); |
| _sll(T1, SrcR, 8); |
| _lui(T2, Ctx->getConstantInt32(255)); |
| _and(T1, T1, T2); |
| _sll(T3, SrcR, 24); |
| _or(T1, T3, T1); |
| _srl(T4, T1, 16); |
| _mov(Dest, T4); |
| return; |
| } |
| case IceType_i32: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| auto *SrcR = legalizeToReg(Src); |
| _srl(T1, SrcR, 24); |
| _srl(T2, SrcR, 8); |
| _andi(T2, T2, 0xFF00); |
| _or(T1, T2, T1); |
| _sll(T4, SrcR, 8); |
| _lui(T3, Ctx->getConstantInt32(255)); |
| _and(T4, T4, T3); |
| _sll(T5, SrcR, 24); |
| _or(T4, T5, T4); |
| _or(T4, T4, T1); |
| _mov(Dest, T4); |
| return; |
| } |
| case IceType_i64: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| auto *T6 = I32Reg(); |
| auto *T7 = I32Reg(); |
| auto *T8 = I32Reg(); |
| auto *T9 = I32Reg(); |
| auto *T10 = I32Reg(); |
| auto *T11 = I32Reg(); |
| auto *T12 = I32Reg(); |
| auto *T13 = I32Reg(); |
| auto *T14 = I32Reg(); |
| auto *T15 = I32Reg(); |
| auto *T16 = I32Reg(); |
| auto *T17 = I32Reg(); |
| auto *T18 = I32Reg(); |
| auto *DestLo = llvm::cast<Variable>(loOperand(Dest)); |
| auto *DestHi = llvm::cast<Variable>(hiOperand(Dest)); |
| Src = legalizeUndef(Src); |
| auto *SrcLoR = legalizeToReg(loOperand(Src)); |
| auto *SrcHiR = legalizeToReg(hiOperand(Src)); |
| _sll(T1, SrcHiR, 8); |
| _srl(T2, SrcHiR, 24); |
| _srl(T3, SrcHiR, 8); |
| _andi(T3, T3, 0xFF00); |
| _lui(T4, Ctx->getConstantInt32(255)); |
| _or(T5, T3, T2); |
| _and(T6, T1, T4); |
| _sll(T7, SrcHiR, 24); |
| _or(T8, T7, T6); |
| _srl(T9, SrcLoR, 24); |
| _srl(T10, SrcLoR, 8); |
| _andi(T11, T10, 0xFF00); |
| _or(T12, T8, T5); |
| _or(T13, T11, T9); |
| _sll(T14, SrcLoR, 8); |
| _and(T15, T14, T4); |
| _sll(T16, SrcLoR, 24); |
| _or(T17, T16, T15); |
| _or(T18, T17, T13); |
| _mov(DestLo, T12); |
| _mov(DestHi, T18); |
| return; |
| } |
| default: |
| llvm::report_fatal_error("Control flow should never have reached here."); |
| } |
| return; |
| } |
| case Intrinsics::Ctpop: { |
| llvm::report_fatal_error("Ctpop should have been prelowered."); |
| return; |
| } |
| case Intrinsics::Ctlz: { |
| auto *Src = Instr->getArg(0); |
| const Type SrcTy = Src->getType(); |
| assert(SrcTy == IceType_i32 || SrcTy == IceType_i64); |
| switch (SrcTy) { |
| case IceType_i32: { |
| auto *T = I32Reg(); |
| auto *SrcR = legalizeToReg(Src); |
| _clz(T, SrcR); |
| _mov(Dest, T); |
| break; |
| } |
| case IceType_i64: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *DestLo = llvm::cast<Variable>(loOperand(Dest)); |
| auto *DestHi = llvm::cast<Variable>(hiOperand(Dest)); |
| Variable *SrcHiR = legalizeToReg(hiOperand(Src)); |
| Variable *SrcLoR = legalizeToReg(loOperand(Src)); |
| _clz(T1, SrcHiR); |
| _clz(T2, SrcLoR); |
| _addiu(T3, T2, 32); |
| _movn(T3, T1, SrcHiR); |
| _addiu(T4, getZero(), 0); |
| _mov(DestHi, T4); |
| _mov(DestLo, T3); |
| break; |
| } |
| default: |
| llvm::report_fatal_error("Control flow should never have reached here."); |
| } |
| break; |
| } |
| case Intrinsics::Cttz: { |
| auto *Src = Instr->getArg(0); |
| const Type SrcTy = Src->getType(); |
| assert(SrcTy == IceType_i32 || SrcTy == IceType_i64); |
| switch (SrcTy) { |
| case IceType_i32: { |
| auto *T1 = I32Reg(); |
| auto *T2 = I32Reg(); |
| auto *T3 = I32Reg(); |
| auto *T4 = I32Reg(); |
| auto *T5 = I32Reg(); |
| auto *T6 = I32Reg(); |
| auto *SrcR = legalizeToReg(Src); |
| _addiu(T1, SrcR, -1); |
| _not(T2, SrcR); |
| _and(T3, T2, T1); |
| _clz(T4, T3); |
| _addiu(T5, getZero(), 32); |
| _subu(T6, T5, T4); |
| _mov(Dest, T6); |
| break; |
| } |
| case IceType_i64: { |
| auto *THi1 = I32Reg(); |
| auto *THi2 = I32Reg(); |
| auto *THi3 = I32Reg(); |
| auto *THi4 = I32Reg(); |
| auto *THi5 = I32Reg(); |
| auto *THi6 = I32Reg(); |
| auto *TLo1 = I32Reg(); |
| auto *TLo2 = I32Reg(); |
| auto *TLo3 = I32Reg(); |
| auto *TLo4 = I32Reg(); |
| auto *TLo5 = I32Reg(); |
| auto *TLo6 = I32Reg(); |
| auto *TResHi = I32Reg(); |
| auto *DestLo = llvm::cast<Variable>(loOperand(Dest)); |
| auto *DestHi = llvm::cast<Variable>(hiOperand(Dest)); |
| Variable *SrcHiR = legalizeToReg(hiOperand(Src)); |
| Variable *SrcLoR = legalizeToReg(loOperand(Src)); |
| _addiu(THi1, SrcHiR, -1); |
| _not(THi2, SrcHiR); |
| _and(THi3, THi2, THi1); |
| _clz(THi4, THi3); |
| _addiu(THi5, getZero(), 64); |
| _subu(THi6, THi5, THi4); |
| _addiu(TLo1, SrcLoR, -1); |
| _not(TLo2, SrcLoR); |
| _and(TLo3, TLo2, TLo1); |
| _clz(TLo4, TLo3); |
| _addiu(TLo5, getZero(), 32); |
| _subu(TLo6, TLo5, TLo4); |
| _movn(THi6, TLo6, SrcLoR); |
| _addiu(TResHi, getZero(), 0); |
| _mov(DestHi, TResHi); |
| _mov(DestLo, THi6); |
| break; |
| } |
| default: |
| llvm::report_fatal_error("Control flow should never have reached here."); |
| } |
| return; |
| } |
| case Intrinsics::Fabs: { |
| if (isScalarFloatingType(DestTy)) { |
| Variable *T = makeReg(DestTy); |
| if (DestTy == IceType_f32) { |
| _abs_s(T, legalizeToReg(Instr->getArg(0))); |
| } else { |
| _abs_d(T, legalizeToReg(Instr->getArg(0))); |
| } |
| _mov(Dest, T); |
| } |
| return; |
| } |
| case Intrinsics::Longjmp: { |
| llvm::report_fatal_error("longjmp should have been prelowered."); |
| return; |
| } |
| case Intrinsics::Memcpy: { |
| llvm::report_fatal_error("memcpy should have been prelowered."); |
| return; |
| } |
| case Intrinsics::Memmove: { |
| llvm::report_fatal_error("memmove should have been prelowered."); |
| return; |
| } |
| case Intrinsics::Memset: { |
| llvm::report_fatal_error("memset should have been prelowered."); |
| return; |
| } |
| case Intrinsics::NaClReadTP: { |
| if (SandboxingType != ST_NaCl) |
| llvm::report_fatal_error("nacl-read-tp should have been prelowered."); |
| else { |
| auto *T8 = makeReg(IceType_i32, RegMIPS32::Reg_T8); |
| Context.insert<InstFakeDef>(T8); |
| Variable *TP = legalizeToReg(OperandMIPS32Mem::create( |
| Func, getPointerType(), T8, |
| llvm::cast<ConstantInteger32>(Ctx->getConstantZero(IceType_i32)))); |
| _mov(Dest, TP); |
| } |
| return; |
| } |
| case Intrinsics::Setjmp: { |
| llvm::report_fatal_error("setjmp should have been prelowered."); |
| return; |
| } |
| case Intrinsics::Sqrt: { |
| if (isScalarFloatingType(DestTy)) { |
| Variable *T = makeReg(DestTy); |
| if (DestTy == IceType_f32) { |
| _sqrt_s(T, legalizeToReg(Instr->getArg(0))); |
| } else { |
| _sqrt_d(T, legalizeToReg(Instr->getArg(0))); |
| } |
| _mov(Dest, T); |
| } else { |
| assert(getFlags().getApplicationBinaryInterface() != ::Ice::ABI_PNaCl); |
| UnimplementedLoweringError(this, Instr); // Not required for PNaCl |
| } |
| return; |
| } |
| case Intrinsics::Stacksave: { |
| Variable *SP = getPhysicalRegister(RegMIPS32::Reg_SP); |
| _mov(Dest, SP); |
| return; |
| } |
| case Intrinsics::Stackrestore: { |
| Variable *Val = legalizeToReg(Instr->getArg(0)); |
| Sandboxer(this).reset_sp(Val); |
| return; |
| } |
| case Intrinsics::Trap: { |
| const uint32_t TrapCodeZero = 0; |
| _teq(getZero(), getZero(), TrapCodeZero); |
| return; |
| } |
| case Intrinsics::LoadSubVector: { |
| UnimplementedLoweringError(this, Instr); // Not required for PNaCl |
| return; |
| } |
| case Intrinsics::StoreSubVector: { |
| UnimplementedLoweringError(this, Instr); // Not required for PNaCl |
| return; |
| } |
| default: // UnknownIntrinsic |
| Func->setError("Unexpected intrinsic"); |
| return; |
| } |
| return; |
| } |
| |
| void TargetMIPS32::lowerLoad(const InstLoad *Instr) { |
| // A Load instruction can be treated the same as an Assign instruction, after |
| // the source operand is transformed into an OperandMIPS32Mem operand. |
| Type Ty = Instr->getDest()->getType(); |
| Operand *Src0 = formMemoryOperand(Instr->getSourceAddress(), Ty); |
| Variable *DestLoad = Instr->getDest(); |
| auto *Assign = InstAssign::create(Func, DestLoad, Src0); |
| lowerAssign(Assign); |
| } |
| |
| namespace { |
| void dumpAddressOpt(const Cfg *Func, const Variable *Base, int32_t Offset, |
| const Inst *Reason) { |
| if (!BuildDefs::dump()) |
| return; |
| if (!Func->isVerbose(IceV_AddrOpt)) |
| return; |
| OstreamLocker _(Func->getContext()); |
| Ostream &Str = Func->getContext()->getStrDump(); |
| Str << "Instruction: "; |
| Reason->dumpDecorated(Func); |
| Str << " results in Base="; |
| if (Base) |
| Base->dump(Func); |
| else |
| Str << "<null>"; |
| Str << ", Offset=" << Offset << "\n"; |
| } |
| |
| bool matchAssign(const VariablesMetadata *VMetadata, Variable **Var, |
| int32_t *Offset, const Inst **Reason) { |
| // Var originates from Var=SrcVar ==> set Var:=SrcVar |
| if (*Var == nullptr) |
| return false; |
| const Inst *VarAssign = VMetadata->getSingleDefinition(*Var); |
| if (!VarAssign) |
| return false; |
| assert(!VMetadata->isMultiDef(*Var)); |
| if (!llvm::isa<InstAssign>(VarAssign)) |
| return false; |
| |
| Operand *SrcOp = VarAssign->getSrc(0); |
| bool Optimized = false; |
| if (auto *SrcVar = llvm::dyn_cast<Variable>(SrcOp)) { |
| if (!VMetadata->isMultiDef(SrcVar) || |
| // TODO: ensure SrcVar stays single-BB |
| false) { |
| Optimized = true; |
| *Var = SrcVar; |
| } else if (auto *Const = llvm::dyn_cast<ConstantInteger32>(SrcOp)) { |
| int32_t MoreOffset = Const->getValue(); |
| int32_t NewOffset = MoreOffset + *Offset; |
| if (Utils::WouldOverflowAdd(*Offset, MoreOffset)) |
| return false; |
| *Var = nullptr; |
| *Offset += NewOffset; |
| Optimized = true; |
| } |
| } |
| |
| if (Optimized) { |
| *Reason = VarAssign; |
| } |
| |
| return Optimized; |
| } |
| |
| bool isAddOrSub(const Inst *Instr, InstArithmetic::OpKind *Kind) { |
| if (const auto *Arith = llvm::dyn_cast<InstArithmetic>(Instr)) { |
| switch (Arith->getOp()) { |
| default: |
| return false; |
| case InstArithmetic::Add: |
| case InstArithmetic::Sub: |
| *Kind = Arith->getOp(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool matchOffsetBase(const VariablesMetadata *VMetadata, Variable **Base, |
| int32_t *Offset, const Inst **Reason) { |
| // Base is Base=Var+Const || Base is Base=Const+Var ==> |
| // set Base=Var, Offset+=Const |
| // Base is Base=Var-Const ==> |
| // set Base=Var, Offset-=Const |
| if (*Base == nullptr) |
| return false; |
| const Inst *BaseInst = VMetadata->getSingleDefinition(*Base); |
| if (BaseInst == nullptr) { |
| return false; |
| } |
| assert(!VMetadata->isMultiDef(*Base)); |
| |
| auto *ArithInst = llvm::dyn_cast<const InstArithmetic>(BaseInst); |
| if (ArithInst == nullptr) |
| return false; |
| InstArithmetic::OpKind Kind; |
| if (!isAddOrSub(ArithInst, &Kind)) |
| return false; |
| bool IsAdd = Kind == InstArithmetic::Add; |
| Operand *Src0 = ArithInst->getSrc(0); |
| Operand *Src1 = ArithInst->getSrc(1); |
| auto *Var0 = llvm::dyn_cast<Variable>(Src0); |
| auto *Var1 = llvm::dyn_cast<Variable>(Src1); |
| auto *Const0 = llvm::dyn_cast<ConstantInteger32>(Src0); |
| auto *Const1 = llvm::dyn_cast<ConstantInteger32>(Src1); |
| Variable *NewBase = nullptr; |
| int32_t NewOffset = *Offset; |
| |
| if (Var0 == nullptr && Const0 == nullptr) { |
| assert(llvm::isa<ConstantRelocatable>(Src0)); |
| return false; |
| } |
| |
| if (Var1 == nullptr && Const1 == nullptr) { |
| assert(llvm::isa<ConstantRelocatable>(Src1)); |
| return false; |
| } |
| |
| if (Var0 && Var1) |
| // TODO(jpp): merge base/index splitting into here. |
| return false; |
| if (!IsAdd && Var1) |
| return false; |
| if (Var0) |
| NewBase = Var0; |
| else if (Var1) |
| NewBase = Var1; |
| // Compute the updated constant offset. |
| if (Const0) { |
| int32_t MoreOffset = IsAdd ? Const0->getValue() : -Const0->getValue(); |
| if (Utils::WouldOverflowAdd(NewOffset, MoreOffset)) |
| return false; |
| NewOffset += MoreOffset; |
| } |
| if (Const1) { |
| int32_t MoreOffset = IsAdd ? Const1->getValue() : -Const1->getValue(); |
| if (Utils::WouldOverflowAdd(NewOffset, MoreOffset)) |
| return false; |
| NewOffset += MoreOffset; |
| } |
| |
| // Update the computed address parameters once we are sure optimization |
| // is valid. |
| *Base = NewBase; |
| *Offset = NewOffset; |
| *Reason = BaseInst; |
| return true; |
| } |
| } // end of anonymous namespace |
| |
| OperandMIPS32Mem *TargetMIPS32::formAddressingMode(Type Ty, Cfg *Func, |
| const Inst *LdSt, |
| Operand *Base) { |
| assert(Base != nullptr); |
| int32_t OffsetImm = 0; |
| |
| Func->resetCurrentNode(); |
| if (Func->isVerbose(IceV_AddrOpt)) { |
| OstreamLocker _(Func->getContext()); |
| Ostream &Str = Func->getContext()->getStrDump(); |
| Str << "\nAddress mode formation:\t"; |
| LdSt->dumpDecorated(Func); |
| } |
| |
| if (isVectorType(Ty)) { |
| return nullptr; |
| } |
| |
| auto *BaseVar = llvm::dyn_cast<Variable>(Base); |
| if (BaseVar == nullptr) |
| return nullptr; |
| |
| const VariablesMetadata *VMetadata = Func->getVMetadata(); |
| const Inst *Reason = nullptr; |
| |
| do { |
| if (Reason != nullptr) { |
| dumpAddressOpt(Func, BaseVar, OffsetImm, Reason); |
| Reason = nullptr; |
| } |
| |
| if (matchAssign(VMetadata, &BaseVar, &OffsetImm, &Reason)) { |
| continue; |
| } |
| |
| if (matchOffsetBase(VMetadata, &BaseVar, &OffsetImm, &Reason)) { |
| continue; |
| } |
| } while (Reason); |
| |
| if (BaseVar == nullptr) { |
| // We need base register rather than just OffsetImm. Move the OffsetImm to |
| // BaseVar and form 0(BaseVar) addressing. |
| const Type PointerType = getPointerType(); |
| BaseVar = makeReg(PointerType); |
| Context.insert<InstAssign>(BaseVar, Ctx->getConstantInt32(OffsetImm)); |
| OffsetImm = 0; |
| } else if (OffsetImm != 0) { |
| // If the OffsetImm is more than signed 16-bit value then add it in the |
| // BaseVar and form 0(BaseVar) addressing. |
| const int32_t PositiveOffset = OffsetImm > 0 ? OffsetImm : -OffsetImm; |
| const InstArithmetic::OpKind Op = |
| OffsetImm > 0 ? InstArithmetic::Add : InstArithmetic::Sub; |
| constexpr bool ZeroExt = false; |
| if (!OperandMIPS32Mem::canHoldOffset(Ty, ZeroExt, OffsetImm)) { |
| const Type PointerType = getPointerType(); |
| Variable *T = makeReg(PointerType); |
| Context.insert<InstArithmetic>(Op, T, BaseVar, |
| Ctx->getConstantInt32(PositiveOffset)); |
| BaseVar = T; |
| OffsetImm = 0; |
| } |
| } |
| |
| assert(BaseVar != nullptr); |
| assert(OffsetImm < 0 ? (-OffsetImm & 0x0000ffff) == -OffsetImm |
| : (OffsetImm & 0x0000ffff) == OffsetImm); |
| |
| return OperandMIPS32Mem::create( |
| Func, Ty, BaseVar, |
| llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(OffsetImm))); |
| } |
| |
| void TargetMIPS32::doAddressOptLoad() { |
| Inst *Instr = iteratorToInst(Context.getCur()); |
| assert(llvm::isa<InstLoad>(Instr)); |
| Variable *Dest = Instr->getDest(); |
| Operand *Addr = Instr->getSrc(0); |
| if (OperandMIPS32Mem *Mem = |
| formAddressingMode(Dest->getType(), Func, Instr, Addr)) { |
| Instr->setDeleted(); |
| Context.insert<InstLoad>(Dest, Mem); |
| } |
| } |
| |
| void TargetMIPS32::randomlyInsertNop(float Probability, |
| RandomNumberGenerator &RNG) { |
| RandomNumberGeneratorWrapper RNGW(RNG); |
| if (RNGW.getTrueWithProbability(Probability)) { |
| _nop(); |
| } |
| } |
| |
| void TargetMIPS32::lowerPhi(const InstPhi * /*Instr*/) { |
| Func->setError("Phi found in regular instruction list"); |
| } |
| |
| void TargetMIPS32::lowerRet(const InstRet *Instr) { |
| Variable *Reg = nullptr; |
| if (Instr->hasRetValue()) { |
| Operand *Src0 = Instr->getRetValue(); |
| switch (Src0->getType()) { |
| case IceType_f32: { |
| Operand *Src0F = legalizeToReg(Src0); |
| Reg = makeReg(Src0F->getType(), RegMIPS32::Reg_F0); |
| _mov(Reg, Src0F); |
| break; |
| } |
| case IceType_f64: { |
| Operand *Src0F = legalizeToReg(Src0); |
| Reg = makeReg(Src0F->getType(), RegMIPS32::Reg_F0F1); |
| _mov(Reg, Src0F); |
| break; |
| } |
| case IceType_i1: |
| case IceType_i8: |
| case IceType_i16: |
| case IceType_i32: { |
| Operand *Src0F = legalizeToReg(Src0); |
| Reg = makeReg(Src0F->getType(), RegMIPS32::Reg_V0); |
| _mov(Reg, Src0F); |
| break; |
| } |
| case IceType_i64: { |
| Src0 = legalizeUndef(Src0); |
| Variable *R0 = legalizeToReg(loOperand(Src0), RegMIPS32::Reg_V0); |
| Variable *R1 = legalizeToReg(hiOperand(Src0), RegMIPS32::Reg_V1); |
| Reg = R0; |
| Context.insert<InstFakeUse>(R1); |
| break; |
| } |
| case IceType_v4i1: |
| case IceType_v8i1: |
| case IceType_v16i1: |
| case IceType_v16i8: |
| case IceType_v8i16: |
| case IceType_v4i32: { |
| auto *SrcVec = llvm::dyn_cast<VariableVecOn32>(legalizeUndef(Src0)); |
| Variable *V0 = |
| legalizeToReg(SrcVec->getContainers()[0], RegMIPS32::Reg_V0); |
| Variable *V1 = |
| legalizeToReg(SrcVec->getContainers()[1], RegMIPS32::Reg_V1); |
| Variable *A0 = |
| legalizeToReg(SrcVec->getContainers()[2], RegMIPS32::Reg_A0); |
| Variable *A1 = |
| legalizeToReg(SrcVec->getContainers()[3], RegMIPS32::Reg_A1); |
| Reg = V0; |
| Context.insert<InstFakeUse>(V1); |
| Context.insert<InstFakeUse>(A0); |
| Context.insert<InstFakeUse>(A1); |
| break; |
| } |
| case IceType_v4f32: { |
| auto *SrcVec = llvm::dyn_cast<VariableVecOn32>(legalizeUndef(Src0)); |
| Reg = getImplicitRet(); |
| auto *RegT = legalizeToReg(Reg); |
| // Return the vector through buffer in implicit argument a0 |
| for (SizeT i = 0; i < SrcVec->ContainersPerVector; ++i) { |
| OperandMIPS32Mem *Mem = OperandMIPS32Mem::create( |
| Func, IceType_f32, RegT, |
| llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(i * 4))); |
| Variable *Var = legalizeToReg(SrcVec->getContainers()[i]); |
| _sw(Var, Mem); |
| } |
| Variable *V0 = makeReg(IceType_i32, RegMIPS32::Reg_V0); |
| _mov(V0, Reg); // move v0,a0 |
| Context.insert<InstFakeUse>(Reg); |
| Context.insert<InstFakeUse>(V0); |
| break; |
| } |
| default: |
| llvm::report_fatal_error("Ret: Invalid type."); |
| break; |
| } |
| } |
| _ret(getPhysicalRegister(RegMIPS32::Reg_RA), Reg); |
| } |
| |
| void TargetMIPS32::lowerSelect(const InstSelect *Instr) { |
| Variable *Dest = Instr->getDest(); |
| const Type DestTy = Dest->getType(); |
| |
| if (isVectorType(DestTy)) { |
| llvm::report_fatal_error("Select: Destination type is vector"); |
| return; |
| } |
| |
| Variable *DestR = nullptr; |
| Variable *DestHiR = nullptr; |
| Variable *SrcTR = nullptr; |
| Variable *SrcTHiR = nullptr; |
| Variable *SrcFR = nullptr; |
| Variable *SrcFHiR = nullptr; |
| |
| if (DestTy == IceType_i64) { |
| DestR = llvm::cast<Variable>(loOperand(Dest)); |
| DestHiR = llvm::cast<Variable>(hiOperand(Dest)); |
| SrcTR = legalizeToReg(loOperand(legalizeUndef(Instr->getTrueOperand()))); |
| SrcTHiR = legalizeToReg(hiOperand(legalizeUndef(Instr->getTrueOperand()))); |
| SrcFR = legalizeToReg(loOperand(legalizeUndef(Instr->getFalseOperand()))); |
| SrcFHiR = legalizeToReg(hiOperand(legalizeUndef(Instr->getFalseOperand()))); |
| } else { |
| SrcTR = legalizeToReg(legalizeUndef(Instr->getTrueOperand())); |
| SrcFR = legalizeToReg(legalizeUndef(Instr->getFalseOperand())); |
| } |
| |
| Variable *ConditionR = legalizeToReg(Instr->getCondition()); |
| |
| assert(Instr->getCondition()->getType() == IceType_i1); |
| |
| switch (DestTy) { |
| case IceType_i1: |
| case IceType_i8: |
| case IceType_i16: |
| case IceType_i32: |
| _movn(SrcFR, SrcTR, ConditionR); |
| _mov(Dest, SrcFR); |
| break; |
| case IceType_i64: |
| _movn(SrcFR, SrcTR, ConditionR); |
| _movn(SrcFHiR, SrcTHiR, ConditionR); |
| _mov(DestR, SrcFR); |
| _mov(DestHiR, SrcFHiR); |
| break; |
| case IceType_f32: |
| _movn_s(SrcFR, SrcTR, ConditionR); |
| _mov(Dest, SrcFR); |
| break; |
| case IceType_f64: |
| _movn_d(SrcFR, SrcTR, ConditionR); |
| _mov(Dest, SrcFR); |
| break; |
| default: |
| llvm::report_fatal_error("Select: Invalid type."); |
| } |
| } |
| |
| void TargetMIPS32::lowerShuffleVector(const InstShuffleVector *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lowerStore(const InstStore *Instr) { |
| Operand *Value = Instr->getData(); |
| Operand *Addr = Instr->getAddr(); |
| OperandMIPS32Mem *NewAddr = formMemoryOperand(Addr, Value->getType()); |
| Type Ty = NewAddr->getType(); |
| |
| if (Ty == IceType_i64) { |
| Value = legalizeUndef(Value); |
| Variable *ValueHi = legalizeToReg(hiOperand(Value)); |
| Variable *ValueLo = legalizeToReg(loOperand(Value)); |
| _sw(ValueHi, llvm::cast<OperandMIPS32Mem>(hiOperand(NewAddr))); |
| _sw(ValueLo, llvm::cast<OperandMIPS32Mem>(loOperand(NewAddr))); |
| } else if (isVectorType(Value->getType())) { |
| auto *DataVec = llvm::dyn_cast<VariableVecOn32>(Value); |
| for (SizeT i = 0; i < DataVec->ContainersPerVector; ++i) { |
| auto *DCont = legalizeToReg(DataVec->getContainers()[i]); |
| auto *MCont = llvm::cast<OperandMIPS32Mem>( |
| getOperandAtIndex(NewAddr, IceType_i32, i)); |
| _sw(DCont, MCont); |
| } |
| } else { |
| Variable *ValueR = legalizeToReg(Value); |
| _sw(ValueR, NewAddr); |
| } |
| } |
| |
| void TargetMIPS32::doAddressOptStore() { |
| Inst *Instr = iteratorToInst(Context.getCur()); |
| assert(llvm::isa<InstStore>(Instr)); |
| Operand *Src = Instr->getSrc(0); |
| Operand *Addr = Instr->getSrc(1); |
| if (OperandMIPS32Mem *Mem = |
| formAddressingMode(Src->getType(), Func, Instr, Addr)) { |
| Instr->setDeleted(); |
| Context.insert<InstStore>(Src, Mem); |
| } |
| } |
| |
| void TargetMIPS32::lowerSwitch(const InstSwitch *Instr) { |
| Operand *Src = Instr->getComparison(); |
| SizeT NumCases = Instr->getNumCases(); |
| if (Src->getType() == IceType_i64) { |
| Src = legalizeUndef(Src); |
| Variable *Src0Lo = legalizeToReg(loOperand(Src)); |
| Variable *Src0Hi = legalizeToReg(hiOperand(Src)); |
| for (SizeT I = 0; I < NumCases; ++I) { |
| Operand *ValueLo = Ctx->getConstantInt32(Instr->getValue(I)); |
| Operand *ValueHi = Ctx->getConstantInt32(Instr->getValue(I) >> 32); |
| CfgNode *TargetTrue = Instr->getLabel(I); |
| constexpr CfgNode *NoTarget = nullptr; |
| ValueHi = legalizeToReg(ValueHi); |
| InstMIPS32Label *IntraLabel = InstMIPS32Label::create(Func, this); |
| _br(NoTarget, NoTarget, Src0Hi, ValueHi, IntraLabel, |
| CondMIPS32::Cond::NE); |
| ValueLo = legalizeToReg(ValueLo); |
| _br(NoTarget, TargetTrue, Src0Lo, ValueLo, CondMIPS32::Cond::EQ); |
| Context.insert(IntraLabel); |
| } |
| _br(Instr->getLabelDefault()); |
| return; |
| } |
| Variable *SrcVar = legalizeToReg(Src); |
| assert(SrcVar->mustHaveReg()); |
| for (SizeT I = 0; I < NumCases; ++I) { |
| Operand *Value = Ctx->getConstantInt32(Instr->getValue(I)); |
| CfgNode *TargetTrue = Instr->getLabel(I); |
| constexpr CfgNode *NoTargetFalse = nullptr; |
| Value = legalizeToReg(Value); |
| _br(NoTargetFalse, TargetTrue, SrcVar, Value, CondMIPS32::Cond::EQ); |
| } |
| _br(Instr->getLabelDefault()); |
| } |
| |
| void TargetMIPS32::lowerBreakpoint(const InstBreakpoint *Instr) { |
| UnimplementedLoweringError(this, Instr); |
| } |
| |
| void TargetMIPS32::lowerUnreachable(const InstUnreachable *) { |
| const uint32_t TrapCodeZero = 0; |
| _teq(getZero(), getZero(), TrapCodeZero); |
| } |
| |
| void TargetMIPS32::lowerOther(const Inst *Instr) { |
| if (llvm::isa<InstMIPS32Sync>(Instr)) { |
| _sync(); |
| } else { |
| TargetLowering::lowerOther(Instr); |
| } |
| } |
| |
| // Turn an i64 Phi instruction into a pair of i32 Phi instructions, to preserve |
| // integrity of liveness analysis. Undef values are also turned into zeroes, |
| // since loOperand() and hiOperand() don't expect Undef input. |
| void TargetMIPS32::prelowerPhis() { |
| PhiLowering::prelowerPhis32Bit<TargetMIPS32>(this, Context.getNode(), Func); |
| } |
| |
| void TargetMIPS32::postLower() { |
| if (Func->getOptLevel() == Opt_m1) |
| return; |
| markRedefinitions(); |
| Context.availabilityUpdate(); |
| } |
| |
| void TargetMIPS32::makeRandomRegisterPermutation( |
| llvm::SmallVectorImpl<RegNumT> &Permutation, |
| const SmallBitVector &ExcludeRegisters, uint64_t Salt) const { |
| (void)Permutation; |
| (void)ExcludeRegisters; |
| (void)Salt; |
| UnimplementedError(getFlags()); |
| } |
| |
| /* TODO(jvoung): avoid duplicate symbols with multiple targets. |
| void ConstantUndef::emitWithoutDollar(GlobalContext *) const { |
| llvm_unreachable("Not expecting to emitWithoutDollar undef"); |
| } |
| |
| void ConstantUndef::emit(GlobalContext *) const { |
| llvm_unreachable("undef value encountered by emitter."); |
| } |
| */ |
| |
| TargetDataMIPS32::TargetDataMIPS32(GlobalContext *Ctx) |
| : TargetDataLowering(Ctx) {} |
| |
| // Generate .MIPS.abiflags section. This section contains a versioned data |
| // structure with essential information required for loader to determine the |
| // requirements of the application. |
| void TargetDataMIPS32::emitTargetRODataSections() { |
| struct MipsABIFlagsSection Flags; |
| ELFObjectWriter *Writer = Ctx->getObjectWriter(); |
| const std::string Name = ".MIPS.abiflags"; |
| const llvm::ELF::Elf64_Word ShType = llvm::ELF::SHT_MIPS_ABIFLAGS; |
| const llvm::ELF::Elf64_Xword ShFlags = llvm::ELF::SHF_ALLOC; |
| const llvm::ELF::Elf64_Xword ShAddralign = 8; |
| const llvm::ELF::Elf64_Xword ShEntsize = sizeof(Flags); |
| Writer->writeTargetRODataSection( |
| Name, ShType, ShFlags, ShAddralign, ShEntsize, |
| llvm::StringRef(reinterpret_cast<const char *>(&Flags), sizeof(Flags))); |
| } |
| |
| void TargetDataMIPS32::lowerGlobals(const VariableDeclarationList &Vars, |
| const std::string &SectionSuffix) { |
| const bool IsPIC = getFlags().getUseNonsfi(); |
| switch (getFlags().getOutFileType()) { |
| case FT_Elf: { |
| ELFObjectWriter *Writer = Ctx->getObjectWriter(); |
| Writer->writeDataSection(Vars, llvm::ELF::R_MIPS_32, SectionSuffix, IsPIC); |
| } break; |
| case FT_Asm: |
| case FT_Iasm: { |
| OstreamLocker L(Ctx); |
| for (const VariableDeclaration *Var : Vars) { |
| if (getFlags().matchTranslateOnly(Var->getName(), 0)) { |
| emitGlobal(*Var, SectionSuffix); |
| } |
| } |
| } break; |
| } |
| } |
| |
| namespace { |
| template <typename T> struct ConstantPoolEmitterTraits; |
| |
| static_assert(sizeof(uint64_t) == 8, |
| "uint64_t is supposed to be 8 bytes wide."); |
| |
| // TODO(jaydeep.patil): implement the following when implementing constant |
| // randomization: |
| // * template <> struct ConstantPoolEmitterTraits<uint8_t> |
| // * template <> struct ConstantPoolEmitterTraits<uint16_t> |
| // * template <> struct ConstantPoolEmitterTraits<uint32_t> |
| template <> struct ConstantPoolEmitterTraits<float> { |
| using ConstantType = ConstantFloat; |
| static constexpr Type IceType = IceType_f32; |
| // AsmTag and TypeName can't be constexpr because llvm::StringRef is unhappy |
| // about them being constexpr. |
| static const char AsmTag[]; |
| static const char TypeName[]; |
| static uint64_t bitcastToUint64(float Value) { |
| static_assert(sizeof(Value) == sizeof(uint32_t), |
| "Float should be 4 bytes."); |
| const uint32_t IntValue = Utils::bitCopy<uint32_t>(Value); |
| return static_cast<uint64_t>(IntValue); |
| } |
| }; |
| const char ConstantPoolEmitterTraits<float>::AsmTag[] = ".word"; |
| const char ConstantPoolEmitterTraits<float>::TypeName[] = "f32"; |
| |
| template <> struct ConstantPoolEmitterTraits<double> { |
| using ConstantType = ConstantDouble; |
| static constexpr Type IceType = IceType_f64; |
| static const char AsmTag[]; |
| static const char TypeName[]; |
| static uint64_t bitcastToUint64(double Value) { |
| static_assert(sizeof(double) == sizeof(uint64_t), |
| "Double should be 8 bytes."); |
| return Utils::bitCopy<uint64_t>(Value); |
| } |
| }; |
| const char ConstantPoolEmitterTraits<double>::AsmTag[] = ".quad"; |
| const char ConstantPoolEmitterTraits<double>::TypeName[] = "f64"; |
| |
| template <typename T> |
| void emitConstant( |
| Ostream &Str, |
| const typename ConstantPoolEmitterTraits<T>::ConstantType *Const) { |
| if (!BuildDefs::dump()) |
| return; |
| using Traits = ConstantPoolEmitterTraits<T>; |
| Str << Const->getLabelName(); |
| T Value = Const->getValue(); |
| Str << ":\n\t" << Traits::AsmTag << "\t0x"; |
| Str.write_hex(Traits::bitcastToUint64(Value)); |
| Str << "\t/* " << Traits::TypeName << " " << Value << " */\n"; |
| } |
| |
| template <typename T> void emitConstantPool(GlobalContext *Ctx) { |
| if (!BuildDefs::dump()) |
| return; |
| using Traits = ConstantPoolEmitterTraits<T>; |
| static constexpr size_t MinimumAlignment = 4; |
| SizeT Align = std::max(MinimumAlignment, typeAlignInBytes(Traits::IceType)); |
| assert((Align % 4) == 0 && "Constants should be aligned"); |
| Ostream &Str = Ctx->getStrEmit(); |
| ConstantList Pool = Ctx->getConstantPool(Traits::IceType); |
| Str << "\t.section\t.rodata.cst" << Align << ",\"aM\",%progbits," << Align |
| << "\n" |
| << "\t.align\t" << (Align == 4 ? 2 : 3) << "\n"; |
| if (getFlags().getReorderPooledConstants()) { |
| // TODO(jaydeep.patil): add constant pooling. |
| UnimplementedError(getFlags()); |
| } |
| for (Constant *C : Pool) { |
| if (!C->getShouldBePooled()) { |
| continue; |
| } |
| emitConstant<T>(Str, llvm::dyn_cast<typename Traits::ConstantType>(C)); |
| } |
| } |
| } // end of anonymous namespace |
| |
| void TargetDataMIPS32::lowerConstants() { |
| if (getFlags().getDisableTranslation()) |
| return; |
| switch (getFlags().getOutFileType()) { |
| case FT_Elf: { |
| ELFObjectWriter *Writer = Ctx->getObjectWriter(); |
| Writer->writeConstantPool<ConstantFloat>(IceType_f32); |
| Writer->writeConstantPool<ConstantDouble>(IceType_f64); |
| } break; |
| case FT_Asm: |
| case FT_Iasm: { |
| OstreamLocker _(Ctx); |
| emitConstantPool<float>(Ctx); |
| emitConstantPool<double>(Ctx); |
| break; |
| } |
| } |
| } |
| |
| void TargetDataMIPS32::lowerJumpTables() { |
| if (getFlags().getDisableTranslation()) |
| return; |
| } |
| |
| // Helper for legalize() to emit the right code to lower an operand to a |
| // register of the appropriate type. |
| Variable *TargetMIPS32::copyToReg(Operand *Src, RegNumT RegNum) { |
| Type Ty = Src->getType(); |
| Variable *Reg = makeReg(Ty, RegNum); |
| if (isVectorType(Ty)) { |
| llvm::report_fatal_error("Invalid copy from vector type."); |
| } else { |
| if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(Src)) { |
| _lw(Reg, Mem); |
| } else { |
| _mov(Reg, Src); |
| } |
| } |
| return Reg; |
| } |
| |
| Operand *TargetMIPS32::legalize(Operand *From, LegalMask Allowed, |
| RegNumT RegNum) { |
| Type Ty = From->getType(); |
| // Assert that a physical register is allowed. To date, all calls |
| // to legalize() allow a physical register. Legal_Flex converts |
| // registers to the right type OperandMIPS32FlexReg as needed. |
| assert(Allowed & Legal_Reg); |
| |
| if (RegNum.hasNoValue()) { |
| if (Variable *Subst = getContext().availabilityGet(From)) { |
| // At this point we know there is a potential substitution available. |
| if (!Subst->isRematerializable() && Subst->mustHaveReg() && |
| !Subst->hasReg()) { |
| // At this point we know the substitution will have a register. |
| if (From->getType() == Subst->getType()) { |
| // At this point we know the substitution's register is compatible. |
| return Subst; |
| } |
| } |
| } |
| } |
| |
| // Go through the various types of operands: |
| // OperandMIPS32Mem, Constant, and Variable. |
| // Given the above assertion, if type of operand is not legal |
| // (e.g., OperandMIPS32Mem and !Legal_Mem), we can always copy |
| // to a register. |
| if (auto *Mem = llvm::dyn_cast<OperandMIPS32Mem>(From)) { |
| // Base must be in a physical register. |
| Variable *Base = Mem->getBase(); |
| ConstantInteger32 *Offset = llvm::cast<ConstantInteger32>(Mem->getOffset()); |
| Variable *RegBase = nullptr; |
| assert(Base); |
| |
| RegBase = llvm::cast<Variable>( |
| legalize(Base, Legal_Reg | Legal_Rematerializable)); |
| |
| if (Offset != nullptr && Offset->getValue() != 0) { |
| static constexpr bool ZeroExt = false; |
| if (!OperandMIPS32Mem::canHoldOffset(Ty, ZeroExt, Offset->getValue())) { |
| llvm::report_fatal_error("Invalid memory offset."); |
| } |
| } |
| |
| // Create a new operand if there was a change. |
| if (Base != RegBase) { |
| Mem = OperandMIPS32Mem::create(Func, Ty, RegBase, Offset, |
| Mem->getAddrMode()); |
| } |
| |
| if (Allowed & Legal_Mem) { |
| From = Mem; |
| } else { |
| Variable *Reg = makeReg(Ty, RegNum); |
| _lw(Reg, Mem); |
| From = Reg; |
| } |
| return From; |
| } |
| |
| if (llvm::isa<Constant>(From)) { |
| if (llvm::isa<ConstantUndef>(From)) { |
| From = legalizeUndef(From, RegNum); |
| if (isVectorType(Ty)) |
| return From; |
| } |
| if (auto *C = llvm::dyn_cast<ConstantRelocatable>(From)) { |
| Variable *Reg = makeReg(Ty, RegNum); |
| Variable *TReg = makeReg(Ty, RegNum); |
| _lui(TReg, C, RO_Hi); |
| _addiu(Reg, TReg, C, RO_Lo); |
| return Reg; |
| } else if (auto *C32 = llvm::dyn_cast<ConstantInteger32>(From)) { |
| const uint32_t Value = C32->getValue(); |
| // Use addiu if the immediate is a 16bit value. Otherwise load it |
| // using a lui-ori instructions. |
| Variable *Reg = makeReg(Ty, RegNum); |
| if (isInt<16>(int32_t(Value))) { |
| Variable *Zero = makeReg(Ty, RegMIPS32::Reg_ZERO); |
| Context.insert<InstFakeDef>(Zero); |
| _addiu(Reg, Zero, Value); |
| } else { |
| uint32_t UpperBits = (Value >> 16) & 0xFFFF; |
| uint32_t LowerBits = Value & 0xFFFF; |
| if (LowerBits) { |
| Variable *TReg = makeReg(Ty, RegNum); |
| _lui(TReg, Ctx->getConstantInt32(UpperBits)); |
| _ori(Reg, TReg, LowerBits); |
| } else { |
| _lui(Reg, Ctx->getConstantInt32(UpperBits)); |
| } |
| } |
| return Reg; |
| } else if (isScalarFloatingType(Ty)) { |
| auto *CFrom = llvm::cast<Constant>(From); |
| Variable *TReg = makeReg(Ty); |
| if (!CFrom->getShouldBePooled()) { |
| // Float/Double constant 0 is not pooled. |
| Context.insert<InstFakeDef>(TReg); |
| _mov(TReg, getZero()); |
| } else { |
| // Load floats/doubles from literal pool. |
| Constant *Offset = Ctx->getConstantSym(0, CFrom->getLabelName()); |
| Variable *TReg1 = makeReg(getPointerType()); |
| _lui(TReg1, Offset, RO_Hi); |
| OperandMIPS32Mem *Addr = |
| OperandMIPS32Mem::create(Func, Ty, TReg1, Offset); |
| if (Ty == IceType_f32) |
| Sandboxer(this).lwc1(TReg, Addr, RO_Lo); |
| else |
| Sandboxer(this).ldc1(TReg, Addr, RO_Lo); |
| } |
| return copyToReg(TReg, RegNum); |
| } |
| } |
| |
| if (auto *Var = llvm::dyn_cast<Variable>(From)) { |
| if (Var->isRematerializable()) { |
| if (Allowed & Legal_Rematerializable) { |
| return From; |
| } |
| |
| Variable *T = makeReg(Var->getType(), RegNum); |
| _mov(T, Var); |
| return T; |
| } |
| // Check if the variable is guaranteed a physical register. This |
| // can happen either when the variable is pre-colored or when it is |
| // assigned infinite weight. |
| bool MustHaveRegister = (Var->hasReg() || Var->mustHaveReg()); |
| // We need a new physical register for the operand if: |
| // Mem is not allowed and Var isn't guaranteed a physical |
| // register, or |
| // RegNum is required and Var->getRegNum() doesn't match. |
| if ((!(Allowed & Legal_Mem) && !MustHaveRegister) || |
| (RegNum.hasValue() && RegNum != Var->getRegNum())) { |
| From = copyToReg(From, RegNum); |
| } |
| return From; |
| } |
| return From; |
| } |
| |
| namespace BoolFolding { |
| // TODO(sagar.thakur): Add remaining instruction kinds to shouldTrackProducer() |
| // and isValidConsumer() |
| bool shouldTrackProducer(const Inst &Instr) { |
| return Instr.getKind() == Inst::Icmp; |
| } |
| |
| bool isValidConsumer(const Inst &Instr) { return Instr.getKind() == Inst::Br; } |
| } // end of namespace BoolFolding |
| |
| void TargetMIPS32::ComputationTracker::recordProducers(CfgNode *Node) { |
| for (Inst &Instr : Node->getInsts()) { |
| if (Instr.isDeleted()) |
| continue; |
| // Check whether Instr is a valid producer. |
| Variable *Dest = Instr.getDest(); |
| if (Dest // only consider instructions with an actual dest var; and |
| && Dest->getType() == IceType_i1 // only bool-type dest vars; and |
| && BoolFolding::shouldTrackProducer(Instr)) { // white-listed instr. |
| KnownComputations.emplace(Dest->getIndex(), |
| ComputationEntry(&Instr, IceType_i1)); |
| } |
| // Check each src variable against the map. |
| FOREACH_VAR_IN_INST(Var, Instr) { |
| SizeT VarNum = Var->getIndex(); |
| auto ComputationIter = KnownComputations.find(VarNum); |
| if (ComputationIter == KnownComputations.end()) { |
| continue; |
| } |
| |
| ++ComputationIter->second.NumUses; |
| switch (ComputationIter->second.ComputationType) { |
| default: |
| KnownComputations.erase(VarNum); |
| continue; |
| case IceType_i1: |
| if (!BoolFolding::isValidConsumer(Instr)) { |
| KnownComputations.erase(VarNum); |
| continue; |
| } |
| break; |
| } |
| |
| if (Instr.isLastUse(Var)) { |
| ComputationIter->second.IsLiveOut = false; |
| } |
| } |
| } |
| |
| for (auto Iter = KnownComputations.begin(), End = KnownComputations.end(); |
| Iter != End;) { |
| // Disable the folding if its dest may be live beyond this block. |
| if (Iter->second.IsLiveOut || Iter->second.NumUses > 1) { |
| Iter = KnownComputations.erase(Iter); |
| continue; |
| } |
| |
| // Mark as "dead" rather than outright deleting. This is so that other |
| // peephole style optimizations during or before lowering have access to |
| // this instruction in undeleted form. See for example |
| // tryOptimizedCmpxchgCmpBr(). |
| Iter->second.Instr->setDead(); |
| ++Iter; |
| } |
| } |
| |
| TargetHeaderMIPS32::TargetHeaderMIPS32(GlobalContext *Ctx) |
| : TargetHeaderLowering(Ctx) {} |
| |
| void TargetHeaderMIPS32::lower() { |
| if (!BuildDefs::dump()) |
| return; |
| OstreamLocker L(Ctx); |
| Ostream &Str = Ctx->getStrEmit(); |
| Str << "\t.set\t" |
| << "nomicromips\n"; |
| Str << "\t.set\t" |
| << "nomips16\n"; |
| Str << "\t.set\t" |
| << "noat\n"; |
| if (getFlags().getUseSandboxing()) |
| Str << "\t.bundle_align_mode 4\n"; |
| } |
| |
| SmallBitVector TargetMIPS32::TypeToRegisterSet[RCMIPS32_NUM]; |
| SmallBitVector TargetMIPS32::TypeToRegisterSetUnfiltered[RCMIPS32_NUM]; |
| SmallBitVector TargetMIPS32::RegisterAliases[RegMIPS32::Reg_NUM]; |
| |
| TargetMIPS32::Sandboxer::Sandboxer(TargetMIPS32 *Target, |
| InstBundleLock::Option BundleOption) |
| : Target(Target), BundleOption(BundleOption) {} |
| |
| TargetMIPS32::Sandboxer::~Sandboxer() {} |
| |
| void TargetMIPS32::Sandboxer::createAutoBundle() { |
| Bundler = makeUnique<AutoBundle>(Target, BundleOption); |
| } |
| |
| void TargetMIPS32::Sandboxer::addiu_sp(uint32_t StackOffset) { |
| Variable *SP = Target->getPhysicalRegister(RegMIPS32::Reg_SP); |
| if (!Target->NeedSandboxing) { |
| Target->_addiu(SP, SP, StackOffset); |
| return; |
| } |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| createAutoBundle(); |
| Target->_addiu(SP, SP, StackOffset); |
| Target->_and(SP, SP, T7); |
| } |
| |
| void TargetMIPS32::Sandboxer::lw(Variable *Dest, OperandMIPS32Mem *Mem) { |
| Variable *Base = Mem->getBase(); |
| if (Target->NeedSandboxing && (Target->getStackReg() != Base->getRegNum()) && |
| (RegMIPS32::Reg_T8 != Base->getRegNum())) { |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| createAutoBundle(); |
| Target->_and(Base, Base, T7); |
| } |
| Target->_lw(Dest, Mem); |
| if (Target->NeedSandboxing && (Dest->getRegNum() == Target->getStackReg())) { |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| Target->_and(Dest, Dest, T7); |
| } |
| } |
| |
| void TargetMIPS32::Sandboxer::ll(Variable *Dest, OperandMIPS32Mem *Mem) { |
| Variable *Base = Mem->getBase(); |
| if (Target->NeedSandboxing && (Target->getStackReg() != Base->getRegNum())) { |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| createAutoBundle(); |
| Target->_and(Base, Base, T7); |
| } |
| Target->_ll(Dest, Mem); |
| if (Target->NeedSandboxing && (Dest->getRegNum() == Target->getStackReg())) { |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| Target->_and(Dest, Dest, T7); |
| } |
| } |
| |
| void TargetMIPS32::Sandboxer::sc(Variable *Dest, OperandMIPS32Mem *Mem) { |
| Variable *Base = Mem->getBase(); |
| if (Target->NeedSandboxing && (Target->getStackReg() != Base->getRegNum())) { |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| createAutoBundle(); |
| Target->_and(Base, Base, T7); |
| } |
| Target->_sc(Dest, Mem); |
| } |
| |
| void TargetMIPS32::Sandboxer::sw(Variable *Dest, OperandMIPS32Mem *Mem) { |
| Variable *Base = Mem->getBase(); |
| if (Target->NeedSandboxing && (Target->getStackReg() != Base->getRegNum())) { |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| createAutoBundle(); |
| Target->_and(Base, Base, T7); |
| } |
| Target->_sw(Dest, Mem); |
| } |
| |
| void TargetMIPS32::Sandboxer::lwc1(Variable *Dest, OperandMIPS32Mem *Mem, |
| RelocOp Reloc) { |
| Variable *Base = Mem->getBase(); |
| if (Target->NeedSandboxing && (Target->getStackReg() != Base->getRegNum())) { |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| createAutoBundle(); |
| Target->_and(Base, Base, T7); |
| } |
| Target->_lwc1(Dest, Mem, Reloc); |
| if (Target->NeedSandboxing && (Dest->getRegNum() == Target->getStackReg())) { |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| Target->_and(Dest, Dest, T7); |
| } |
| } |
| |
| void TargetMIPS32::Sandboxer::ldc1(Variable *Dest, OperandMIPS32Mem *Mem, |
| RelocOp Reloc) { |
| Variable *Base = Mem->getBase(); |
| if (Target->NeedSandboxing && (Target->getStackReg() != Base->getRegNum())) { |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| createAutoBundle(); |
| Target->_and(Base, Base, T7); |
| } |
| Target->_ldc1(Dest, Mem, Reloc); |
| if (Target->NeedSandboxing && (Dest->getRegNum() == Target->getStackReg())) { |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| Target->_and(Dest, Dest, T7); |
| } |
| } |
| |
| void TargetMIPS32::Sandboxer::ret(Variable *RetAddr, Variable *RetValue) { |
| if (!Target->NeedSandboxing) { |
| Target->_ret(RetAddr, RetValue); |
| } |
| auto *T6 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T6); |
| Target->Context.insert<InstFakeDef>(T6); |
| createAutoBundle(); |
| Target->_and(RetAddr, RetAddr, T6); |
| Target->_ret(RetAddr, RetValue); |
| } |
| |
| void TargetMIPS32::Sandboxer::reset_sp(Variable *Src) { |
| Variable *SP = Target->getPhysicalRegister(RegMIPS32::Reg_SP); |
| if (!Target->NeedSandboxing) { |
| Target->_mov(SP, Src); |
| return; |
| } |
| auto *T7 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T7); |
| Target->Context.insert<InstFakeDef>(T7); |
| createAutoBundle(); |
| Target->_mov(SP, Src); |
| Target->_and(SP, SP, T7); |
| Target->getContext().insert<InstFakeUse>(SP); |
| } |
| |
| InstMIPS32Call *TargetMIPS32::Sandboxer::jal(Variable *ReturnReg, |
| Operand *CallTarget) { |
| if (Target->NeedSandboxing) { |
| createAutoBundle(); |
| if (auto *CallTargetR = llvm::dyn_cast<Variable>(CallTarget)) { |
| auto *T6 = Target->makeReg(IceType_i32, RegMIPS32::Reg_T6); |
| Target->Context.insert<InstFakeDef>(T6); |
| Target->_and(CallTargetR, CallTargetR, T6); |
| } |
| } |
| return Target->Context.insert<InstMIPS32Call>(ReturnReg, CallTarget); |
| } |
| |
| } // end of namespace MIPS32 |
| } // end of namespace Ice |