Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 //===- subzero/src/IceTargetLoweringARM32.cpp - ARM32 lowering ------------===// | 1 //===- subzero/src/IceTargetLoweringARM32.cpp - ARM32 lowering ------------===// |
| 2 // | 2 // |
| 3 // The Subzero Code Generator | 3 // The Subzero Code Generator |
| 4 // | 4 // |
| 5 // This file is distributed under the University of Illinois Open Source | 5 // This file is distributed under the University of Illinois Open Source |
| 6 // License. See LICENSE.TXT for details. | 6 // License. See LICENSE.TXT for details. |
| 7 // | 7 // |
| 8 //===----------------------------------------------------------------------===// | 8 //===----------------------------------------------------------------------===// |
| 9 /// | 9 /// |
| 10 /// \file | 10 /// \file |
| (...skipping 443 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 454 "Infinite-weight Variable has no register assigned"); | 454 "Infinite-weight Variable has no register assigned"); |
| 455 } | 455 } |
| 456 int32_t Offset = Var->getStackOffset(); | 456 int32_t Offset = Var->getStackOffset(); |
| 457 int32_t BaseRegNum = Var->getBaseRegNum(); | 457 int32_t BaseRegNum = Var->getBaseRegNum(); |
| 458 if (BaseRegNum == Variable::NoRegister) { | 458 if (BaseRegNum == Variable::NoRegister) { |
| 459 BaseRegNum = getFrameOrStackReg(); | 459 BaseRegNum = getFrameOrStackReg(); |
| 460 if (!hasFramePointer()) | 460 if (!hasFramePointer()) |
| 461 Offset += getStackAdjustment(); | 461 Offset += getStackAdjustment(); |
| 462 } | 462 } |
| 463 const Type VarTy = Var->getType(); | 463 const Type VarTy = Var->getType(); |
| 464 if (!isLegalVariableStackOffset(VarTy, Offset)) { | 464 if (!isLegalMemOffset(VarTy, Offset)) { |
| 465 llvm::report_fatal_error("Illegal stack offset"); | 465 llvm::report_fatal_error("Illegal stack offset"); |
| 466 } | 466 } |
| 467 Str << "[" << getRegName(BaseRegNum, VarTy); | 467 Str << "[" << getRegName(BaseRegNum, VarTy); |
| 468 if (Offset != 0) { | 468 if (Offset != 0) { |
| 469 Str << ", " << getConstantPrefix() << Offset; | 469 Str << ", " << getConstantPrefix() << Offset; |
| 470 } | 470 } |
| 471 Str << "]"; | 471 Str << "]"; |
| 472 } | 472 } |
| 473 | 473 |
| 474 bool TargetARM32::CallingConv::I64InRegs(std::pair<int32_t, int32_t> *Regs) { | 474 bool TargetARM32::CallingConv::I64InRegs(std::pair<int32_t, int32_t> *Regs) { |
| (...skipping 461 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 936 Variable *RetValue = nullptr; | 936 Variable *RetValue = nullptr; |
| 937 if (RI->getSrcSize()) | 937 if (RI->getSrcSize()) |
| 938 RetValue = llvm::cast<Variable>(RI->getSrc(0)); | 938 RetValue = llvm::cast<Variable>(RI->getSrc(0)); |
| 939 _bundle_lock(); | 939 _bundle_lock(); |
| 940 _bic(LR, LR, RetMask); | 940 _bic(LR, LR, RetMask); |
| 941 _ret(LR, RetValue); | 941 _ret(LR, RetValue); |
| 942 _bundle_unlock(); | 942 _bundle_unlock(); |
| 943 RI->setDeleted(); | 943 RI->setDeleted(); |
| 944 } | 944 } |
| 945 | 945 |
| 946 bool TargetARM32::isLegalVariableStackOffset(Type Ty, int32_t Offset) const { | 946 bool TargetARM32::isLegalMemOffset(Type Ty, int32_t Offset) const { |
| 947 constexpr bool SignExt = false; | 947 constexpr bool ZeroExt = false; |
| 948 return OperandARM32Mem::canHoldOffset(Ty, SignExt, Offset); | 948 return OperandARM32Mem::canHoldOffset(Ty, ZeroExt, Offset); |
| 949 } | 949 } |
| 950 | 950 |
| 951 StackVariable *TargetARM32::legalizeVariableSlot(Variable *Var, | 951 Variable *TargetARM32::newBaseRegister(int32_t OriginalOffset, |
| 952 int32_t StackAdjust, | 952 int32_t StackAdjust, |
| 953 Variable *OrigBaseReg) { | 953 Variable *OrigBaseReg) { |
| 954 int32_t Offset = Var->getStackOffset() + StackAdjust; | 954 int32_t Offset = OriginalOffset + StackAdjust; |
| 955 // Legalize will likely need a movw/movt combination, but if the top bits are | 955 // Legalize will likely need a movw/movt combination, but if the top bits are |
| 956 // all 0 from negating the offset and subtracting, we could use that instead. | 956 // all 0 from negating the offset and subtracting, we could use that instead. |
| 957 bool ShouldSub = (-Offset & 0xFFFF0000) == 0; | 957 bool ShouldSub = (-Offset & 0xFFFF0000) == 0; |
| 958 if (ShouldSub) | 958 if (ShouldSub) |
| 959 Offset = -Offset; | 959 Offset = -Offset; |
| 960 Operand *OffsetVal = legalize(Ctx->getConstantInt32(Offset), | 960 Operand *OffsetVal = legalize(Ctx->getConstantInt32(Offset), |
| 961 Legal_Reg | Legal_Flex, getReservedTmpReg()); | 961 Legal_Reg | Legal_Flex, getReservedTmpReg()); |
| 962 Variable *ScratchReg = makeReg(IceType_i32, getReservedTmpReg()); | 962 Variable *ScratchReg = makeReg(IceType_i32, getReservedTmpReg()); |
| 963 if (ShouldSub) | 963 if (ShouldSub) |
| 964 _sub(ScratchReg, OrigBaseReg, OffsetVal); | 964 _sub(ScratchReg, OrigBaseReg, OffsetVal); |
| 965 else | 965 else |
| 966 _add(ScratchReg, OrigBaseReg, OffsetVal); | 966 _add(ScratchReg, OrigBaseReg, OffsetVal); |
| 967 StackVariable *NewVar = Func->makeVariable<StackVariable>(stackSlotType()); | 967 return ScratchReg; |
| 968 NewVar->setMustNotHaveReg(); | 968 } |
| 969 NewVar->setBaseRegNum(ScratchReg->getRegNum()); | 969 |
| 970 constexpr int32_t NewOffset = 0; | 970 StackVariable *TargetARM32::legalizeStackSlot(Type Ty, int32_t Offset, |
| 971 NewVar->setStackOffset(NewOffset); | 971 int32_t StackAdjust, |
| 972 return NewVar; | 972 Variable *OrigBaseReg, |
| 973 Variable **NewBaseReg, | |
| 974 int32_t *NewBaseOffset) { | |
|
Karl
2015/11/06 21:11:01
Why not int32_t &NewBaseOffset?
John
2015/11/09 22:42:25
Because the method changes NewBaseOffset inside.
| |
| 975 if (*NewBaseReg == nullptr) { | |
| 976 *NewBaseReg = newBaseRegister(Offset, StackAdjust, OrigBaseReg); | |
| 977 *NewBaseOffset = Offset + StackAdjust; | |
| 978 } | |
| 979 | |
| 980 int32_t OffsetDiff = Offset + StackAdjust - *NewBaseOffset; | |
| 981 if (!isLegalMemOffset(Ty, OffsetDiff)) { | |
| 982 *NewBaseReg = newBaseRegister(Offset, StackAdjust, OrigBaseReg); | |
| 983 *NewBaseOffset = Offset + StackAdjust; | |
| 984 OffsetDiff = 0; | |
| 985 } | |
| 986 | |
| 987 StackVariable *NewDest = Func->makeVariable<StackVariable>(Ty); | |
| 988 NewDest->setMustNotHaveReg(); | |
| 989 NewDest->setBaseRegNum((*NewBaseReg)->getRegNum()); | |
| 990 NewDest->setStackOffset(OffsetDiff); | |
| 991 return NewDest; | |
| 992 } | |
| 993 | |
| 994 void TargetARM32::legalizeMovStackAddrImm(InstARM32Mov *MovInstr, | |
| 995 int32_t StackAdjust, | |
| 996 Variable *OrigBaseReg, | |
| 997 Variable **NewBaseReg, | |
| 998 int32_t *NewBaseOffset) { | |
| 999 Variable *Dest = MovInstr->getDest(); | |
| 1000 assert(Dest != nullptr); | |
| 1001 Type DestTy = Dest->getType(); | |
| 1002 assert(DestTy != IceType_i64); | |
| 1003 | |
| 1004 Operand *Src = MovInstr->getSrc(0); | |
| 1005 Type SrcTy = Src->getType(); | |
| 1006 assert(SrcTy != IceType_i64); | |
| 1007 | |
| 1008 if (MovInstr->isMultiDest() || MovInstr->isMultiSource()) | |
| 1009 return; | |
| 1010 | |
| 1011 bool Legalized = false; | |
| 1012 if (!Dest->hasReg()) { | |
| 1013 assert(llvm::cast<Variable>(Src)->hasReg()); | |
| 1014 const int32_t Offset = Dest->getStackOffset(); | |
| 1015 if (!isLegalMemOffset(DestTy, Offset + StackAdjust)) { | |
| 1016 Legalized = true; | |
| 1017 Dest = legalizeStackSlot(DestTy, Offset, StackAdjust, OrigBaseReg, | |
| 1018 NewBaseReg, NewBaseOffset); | |
| 1019 } | |
| 1020 } else if (auto *Var = llvm::dyn_cast<Variable>(Src)) { | |
| 1021 if (!Var->hasReg()) { | |
| 1022 const int32_t Offset = Var->getStackOffset(); | |
| 1023 if (!isLegalMemOffset(SrcTy, Offset + StackAdjust)) { | |
| 1024 Legalized = true; | |
| 1025 Src = legalizeStackSlot(SrcTy, Offset, StackAdjust, OrigBaseReg, | |
| 1026 NewBaseReg, NewBaseOffset); | |
| 1027 } | |
| 1028 } | |
| 1029 } else if (auto *Mem = llvm::dyn_cast<OperandARM32Mem>(Src)) { | |
| 1030 if (ConstantInteger32 *OffsetOp = Mem->getOffset()) { | |
| 1031 const int32_t Offset = OffsetOp->getValue(); | |
| 1032 if (!isLegalMemOffset(SrcTy, Offset + StackAdjust)) { | |
| 1033 assert(Mem->getBase()->hasReg()); | |
| 1034 assert(Mem->getBase()->getRegNum() == (int)getFrameOrStackReg()); | |
|
Jim Stichnoth
2015/11/08 20:05:51
int32_t for consistency
John
2015/11/09 22:42:24
Done.
| |
| 1035 Legalized = true; | |
| 1036 Src = legalizeStackSlot(SrcTy, Offset, StackAdjust, OrigBaseReg, | |
| 1037 NewBaseReg, NewBaseOffset); | |
| 1038 } | |
| 1039 } | |
| 1040 } | |
| 1041 | |
| 1042 if (Legalized) { | |
| 1043 _mov(Dest, Src); | |
| 1044 MovInstr->setDeleted(); | |
| 1045 } | |
| 973 } | 1046 } |
| 974 | 1047 |
| 975 void TargetARM32::legalizeStackSlots() { | 1048 void TargetARM32::legalizeStackSlots() { |
| 976 // If a stack variable's frame offset doesn't fit, convert from: | 1049 // If a stack variable's frame offset doesn't fit, convert from: |
| 977 // ldr X, OFF[SP] | 1050 // ldr X, OFF[SP] |
| 978 // to: | 1051 // to: |
| 979 // movw/movt TMP, OFF_PART | 1052 // movw/movt TMP, OFF_PART |
| 980 // add TMP, TMP, SP | 1053 // add TMP, TMP, SP |
| 981 // ldr X, OFF_MORE[TMP] | 1054 // ldr X, OFF_MORE[TMP] |
| 982 // | 1055 // |
| 983 // This is safe because we have reserved TMP, and add for ARM does not | 1056 // This is safe because we have reserved TMP, and add for ARM does not |
| 984 // clobber the flags register. | 1057 // clobber the flags register. |
| 985 Func->dump("Before legalizeStackSlots"); | 1058 Func->dump("Before legalizeStackSlots"); |
| 986 assert(hasComputedFrame()); | 1059 assert(hasComputedFrame()); |
| 987 // Early exit, if SpillAreaSizeBytes is really small. | 1060 // Early exit, if SpillAreaSizeBytes is really small. |
| 988 // TODO(jpp): this is not safe -- loads and stores of q registers can't have | 1061 // TODO(jpp): this is not safe -- loads and stores of q registers can't have |
| 989 // offsets. | 1062 // offsets. |
| 990 if (isLegalVariableStackOffset(IceType_v4i32, SpillAreaSizeBytes)) | 1063 if (isLegalMemOffset(IceType_v4i32, SpillAreaSizeBytes)) |
| 991 return; | 1064 return; |
| 992 Variable *OrigBaseReg = getPhysicalRegister(getFrameOrStackReg()); | 1065 Variable *OrigBaseReg = getPhysicalRegister(getFrameOrStackReg()); |
| 993 int32_t StackAdjust = 0; | 1066 int32_t StackAdjust = 0; |
| 994 // Do a fairly naive greedy clustering for now. Pick the first stack slot | 1067 // Do a fairly naive greedy clustering for now. Pick the first stack slot |
| 995 // that's out of bounds and make a new base reg using the architecture's temp | 1068 // that's out of bounds and make a new base reg using the architecture's temp |
| 996 // register. If that works for the next slot, then great. Otherwise, create a | 1069 // register. If that works for the next slot, then great. Otherwise, create a |
| 997 // new base register, clobbering the previous base register. Never share a | 1070 // new base register, clobbering the previous base register. Never share a |
| 998 // base reg across different basic blocks. This isn't ideal if local and | 1071 // base reg across different basic blocks. This isn't ideal if local and |
| 999 // multi-block variables are far apart and their references are interspersed. | 1072 // multi-block variables are far apart and their references are interspersed. |
| 1000 // It may help to be more coordinated about assign stack slot numbers and may | 1073 // It may help to be more coordinated about assign stack slot numbers and may |
| 1001 // help to assign smaller offsets to higher-weight variables so that they | 1074 // help to assign smaller offsets to higher-weight variables so that they |
| 1002 // don't depend on this legalization. | 1075 // don't depend on this legalization. |
| 1003 for (CfgNode *Node : Func->getNodes()) { | 1076 for (CfgNode *Node : Func->getNodes()) { |
| 1004 Context.init(Node); | 1077 Context.init(Node); |
| 1005 StackVariable *NewBaseReg = nullptr; | 1078 Variable *NewBaseReg = nullptr; |
| 1006 int32_t NewBaseOffset = 0; | 1079 int32_t NewBaseOffset = 0; |
| 1007 while (!Context.atEnd()) { | 1080 while (!Context.atEnd()) { |
| 1008 PostIncrLoweringContext PostIncrement(Context); | 1081 PostIncrLoweringContext PostIncrement(Context); |
| 1009 Inst *CurInstr = Context.getCur(); | 1082 Inst *CurInstr = Context.getCur(); |
| 1010 Variable *Dest = CurInstr->getDest(); | 1083 Variable *Dest = CurInstr->getDest(); |
| 1084 | |
| 1011 // Check if the previous NewBaseReg is clobbered, and reset if needed. | 1085 // Check if the previous NewBaseReg is clobbered, and reset if needed. |
| 1012 if ((Dest && NewBaseReg && Dest->hasReg() && | 1086 if ((Dest && NewBaseReg && Dest->hasReg() && |
| 1013 Dest->getRegNum() == NewBaseReg->getBaseRegNum()) || | 1087 Dest->getRegNum() == NewBaseReg->getBaseRegNum()) || |
| 1014 llvm::isa<InstFakeKill>(CurInstr)) { | 1088 llvm::isa<InstFakeKill>(CurInstr)) { |
| 1015 NewBaseReg = nullptr; | 1089 NewBaseReg = nullptr; |
| 1016 NewBaseOffset = 0; | 1090 NewBaseOffset = 0; |
| 1017 } | 1091 } |
| 1092 | |
| 1018 // The stack adjustment only matters if we are using SP instead of FP. | 1093 // The stack adjustment only matters if we are using SP instead of FP. |
| 1019 if (!hasFramePointer()) { | 1094 if (!hasFramePointer()) { |
| 1020 if (auto *AdjInst = llvm::dyn_cast<InstARM32AdjustStack>(CurInstr)) { | 1095 if (auto *AdjInst = llvm::dyn_cast<InstARM32AdjustStack>(CurInstr)) { |
| 1021 StackAdjust += AdjInst->getAmount(); | 1096 StackAdjust += AdjInst->getAmount(); |
| 1022 NewBaseOffset += AdjInst->getAmount(); | 1097 NewBaseOffset += AdjInst->getAmount(); |
| 1023 continue; | 1098 continue; |
| 1024 } | 1099 } |
| 1025 if (llvm::isa<InstARM32Call>(CurInstr)) { | 1100 if (llvm::isa<InstARM32Call>(CurInstr)) { |
| 1026 NewBaseOffset -= StackAdjust; | 1101 NewBaseOffset -= StackAdjust; |
| 1027 StackAdjust = 0; | 1102 StackAdjust = 0; |
| 1028 continue; | 1103 continue; |
| 1029 } | 1104 } |
| 1030 } | 1105 } |
| 1031 | 1106 |
| 1032 // For now, only Mov instructions can have stack variables. We need to | 1107 // The Lowering ensures that ldr and str always have legal Mem operands. |
| 1033 // know the type of instruction because we currently create a fresh one | 1108 // The only other instruction that may access memory is mov. |
| 1034 // to replace Dest/Source, rather than mutate in place. | |
| 1035 bool MayNeedOffsetRewrite = false; | |
| 1036 if (auto *MovInstr = llvm::dyn_cast<InstARM32Mov>(CurInstr)) { | 1109 if (auto *MovInstr = llvm::dyn_cast<InstARM32Mov>(CurInstr)) { |
| 1037 MayNeedOffsetRewrite = | 1110 legalizeMovStackAddrImm(MovInstr, StackAdjust, OrigBaseReg, &NewBaseReg, |
| 1038 !MovInstr->isMultiDest() && !MovInstr->isMultiSource(); | 1111 &NewBaseOffset); |
| 1039 } | |
| 1040 | |
| 1041 if (!MayNeedOffsetRewrite) { | |
| 1042 continue; | |
| 1043 } | |
| 1044 | |
| 1045 assert(Dest != nullptr); | |
| 1046 Type DestTy = Dest->getType(); | |
| 1047 assert(DestTy != IceType_i64); | |
| 1048 if (!Dest->hasReg()) { | |
| 1049 int32_t Offset = Dest->getStackOffset(); | |
| 1050 Offset += StackAdjust; | |
| 1051 if (!isLegalVariableStackOffset(DestTy, Offset)) { | |
| 1052 if (NewBaseReg) { | |
| 1053 int32_t OffsetDiff = Offset - NewBaseOffset; | |
| 1054 if (isLegalVariableStackOffset(DestTy, OffsetDiff)) { | |
| 1055 StackVariable *NewDest = | |
| 1056 Func->makeVariable<StackVariable>(stackSlotType()); | |
| 1057 NewDest->setMustNotHaveReg(); | |
| 1058 NewDest->setBaseRegNum(NewBaseReg->getBaseRegNum()); | |
| 1059 NewDest->setStackOffset(OffsetDiff); | |
| 1060 Variable *NewDestVar = NewDest; | |
| 1061 _mov(NewDestVar, CurInstr->getSrc(0)); | |
| 1062 CurInstr->setDeleted(); | |
| 1063 continue; | |
| 1064 } | |
| 1065 } | |
| 1066 StackVariable *LegalDest = | |
| 1067 legalizeVariableSlot(Dest, StackAdjust, OrigBaseReg); | |
| 1068 assert(LegalDest != Dest); | |
| 1069 Variable *LegalDestVar = LegalDest; | |
| 1070 _mov(LegalDestVar, CurInstr->getSrc(0)); | |
| 1071 CurInstr->setDeleted(); | |
| 1072 NewBaseReg = LegalDest; | |
| 1073 NewBaseOffset = Offset; | |
| 1074 continue; | |
| 1075 } | |
| 1076 } | |
| 1077 assert(CurInstr->getSrcSize() == 1); | |
| 1078 Variable *Var = llvm::dyn_cast<Variable>(CurInstr->getSrc(0)); | |
| 1079 if (Var && !Var->hasReg()) { | |
| 1080 Type VarTy = Var->getType(); | |
| 1081 int32_t Offset = Var->getStackOffset(); | |
| 1082 Offset += StackAdjust; | |
| 1083 if (!isLegalVariableStackOffset(VarTy, Offset)) { | |
| 1084 if (NewBaseReg) { | |
| 1085 int32_t OffsetDiff = Offset - NewBaseOffset; | |
| 1086 if (isLegalVariableStackOffset(VarTy, OffsetDiff)) { | |
| 1087 StackVariable *NewVar = | |
| 1088 Func->makeVariable<StackVariable>(stackSlotType()); | |
| 1089 NewVar->setMustNotHaveReg(); | |
| 1090 NewVar->setBaseRegNum(NewBaseReg->getBaseRegNum()); | |
| 1091 NewVar->setStackOffset(OffsetDiff); | |
| 1092 _mov(Dest, NewVar); | |
| 1093 CurInstr->setDeleted(); | |
| 1094 continue; | |
| 1095 } | |
| 1096 } | |
| 1097 StackVariable *LegalVar = | |
| 1098 legalizeVariableSlot(Var, StackAdjust, OrigBaseReg); | |
| 1099 assert(LegalVar != Var); | |
| 1100 _mov(Dest, LegalVar); | |
| 1101 CurInstr->setDeleted(); | |
| 1102 NewBaseReg = LegalVar; | |
| 1103 NewBaseOffset = Offset; | |
| 1104 continue; | |
| 1105 } | |
| 1106 } | 1112 } |
| 1107 } | 1113 } |
| 1108 } | 1114 } |
| 1109 } | 1115 } |
| 1110 | 1116 |
| 1111 Operand *TargetARM32::loOperand(Operand *Operand) { | 1117 Operand *TargetARM32::loOperand(Operand *Operand) { |
| 1112 assert(Operand->getType() == IceType_i64); | 1118 assert(Operand->getType() == IceType_i64); |
| 1113 if (Operand->getType() != IceType_i64) | 1119 if (Operand->getType() != IceType_i64) |
| 1114 return Operand; | 1120 return Operand; |
| 1115 if (auto *Var64On32 = llvm::dyn_cast<Variable64On32>(Operand)) | 1121 if (auto *Var64On32 = llvm::dyn_cast<Variable64On32>(Operand)) |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1160 lowerArithmetic(InstArithmetic::create(Func, InstArithmetic::Add, NewBase, | 1166 lowerArithmetic(InstArithmetic::create(Func, InstArithmetic::Add, NewBase, |
| 1161 Base, Four)); | 1167 Base, Four)); |
| 1162 return OperandARM32Mem::create(Func, SplitType, NewBase, Mem->getIndex(), | 1168 return OperandARM32Mem::create(Func, SplitType, NewBase, Mem->getIndex(), |
| 1163 Mem->getShiftOp(), Mem->getShiftAmt(), | 1169 Mem->getShiftOp(), Mem->getShiftAmt(), |
| 1164 Mem->getAddrMode()); | 1170 Mem->getAddrMode()); |
| 1165 } else { | 1171 } else { |
| 1166 Variable *Base = Mem->getBase(); | 1172 Variable *Base = Mem->getBase(); |
| 1167 ConstantInteger32 *Offset = Mem->getOffset(); | 1173 ConstantInteger32 *Offset = Mem->getOffset(); |
| 1168 assert(!Utils::WouldOverflowAdd(Offset->getValue(), 4)); | 1174 assert(!Utils::WouldOverflowAdd(Offset->getValue(), 4)); |
| 1169 int32_t NextOffsetVal = Offset->getValue() + 4; | 1175 int32_t NextOffsetVal = Offset->getValue() + 4; |
| 1170 const bool SignExt = false; | 1176 const bool ZeroExt = false; |
|
Karl
2015/11/06 21:11:01
constexpr?
John
2015/11/09 22:42:25
Done.
| |
| 1171 if (!OperandARM32Mem::canHoldOffset(SplitType, SignExt, NextOffsetVal)) { | 1177 if (!OperandARM32Mem::canHoldOffset(SplitType, ZeroExt, NextOffsetVal)) { |
| 1172 // We have to make a temp variable and add 4 to either Base or Offset. | 1178 // We have to make a temp variable and add 4 to either Base or Offset. |
| 1173 // If we add 4 to Offset, this will convert a non-RegReg addressing | 1179 // If we add 4 to Offset, this will convert a non-RegReg addressing |
| 1174 // mode into a RegReg addressing mode. Since NaCl sandboxing disallows | 1180 // mode into a RegReg addressing mode. Since NaCl sandboxing disallows |
| 1175 // RegReg addressing modes, prefer adding to base and replacing | 1181 // RegReg addressing modes, prefer adding to base and replacing |
| 1176 // instead. Thus we leave the old offset alone. | 1182 // instead. Thus we leave the old offset alone. |
| 1177 Constant *Four = Ctx->getConstantInt32(4); | 1183 Constant *Four = Ctx->getConstantInt32(4); |
| 1178 Variable *NewBase = Func->makeVariable(Base->getType()); | 1184 Variable *NewBase = Func->makeVariable(Base->getType()); |
| 1179 lowerArithmetic(InstArithmetic::create(Func, InstArithmetic::Add, | 1185 lowerArithmetic(InstArithmetic::create(Func, InstArithmetic::Add, |
| 1180 NewBase, Base, Four)); | 1186 NewBase, Base, Four)); |
| 1181 Base = NewBase; | 1187 Base = NewBase; |
| (...skipping 626 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1808 if (Instr->isUnconditional()) { | 1814 if (Instr->isUnconditional()) { |
| 1809 _br(Instr->getTargetUnconditional()); | 1815 _br(Instr->getTargetUnconditional()); |
| 1810 return; | 1816 return; |
| 1811 } | 1817 } |
| 1812 Operand *Cond = Instr->getCondition(); | 1818 Operand *Cond = Instr->getCondition(); |
| 1813 | 1819 |
| 1814 CondARM32::Cond BrCondTrue0 = CondARM32::NE; | 1820 CondARM32::Cond BrCondTrue0 = CondARM32::NE; |
| 1815 CondARM32::Cond BrCondTrue1 = CondARM32::kNone; | 1821 CondARM32::Cond BrCondTrue1 = CondARM32::kNone; |
| 1816 CondARM32::Cond BrCondFalse = CondARM32::kNone; | 1822 CondARM32::Cond BrCondFalse = CondARM32::kNone; |
| 1817 if (!_mov_i1_to_flags(Cond, &BrCondTrue0, &BrCondTrue1, &BrCondFalse)) { | 1823 if (!_mov_i1_to_flags(Cond, &BrCondTrue0, &BrCondTrue1, &BrCondFalse)) { |
| 1818 // "Cond" was not fold. | 1824 // "Cond" was not folded. |
| 1819 Type Ty = Cond->getType(); | 1825 Type Ty = Cond->getType(); |
| 1820 Variable *Src0R = legalizeToReg(Cond); | 1826 Variable *Src0R = legalizeToReg(Cond); |
| 1821 assert(Ty == IceType_i1); | 1827 assert(Ty == IceType_i1); |
| 1822 if (Ty != IceType_i32) | 1828 if (Ty != IceType_i32) |
| 1823 _uxt(Src0R, Src0R); | 1829 _uxt(Src0R, Src0R); |
| 1824 Constant *_0 = Ctx->getConstantZero(IceType_i32); | 1830 Constant *_0 = Ctx->getConstantZero(IceType_i32); |
| 1825 _cmp(Src0R, _0); | 1831 _cmp(Src0R, _0); |
| 1826 BrCondTrue0 = CondARM32::NE; | 1832 BrCondTrue0 = CondARM32::NE; |
| 1827 } | 1833 } |
| 1828 | 1834 |
| (...skipping 1535 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3364 Type Ty = Load->getDest()->getType(); | 3370 Type Ty = Load->getDest()->getType(); |
| 3365 Operand *Src0 = formMemoryOperand(Load->getSourceAddress(), Ty); | 3371 Operand *Src0 = formMemoryOperand(Load->getSourceAddress(), Ty); |
| 3366 Variable *DestLoad = Load->getDest(); | 3372 Variable *DestLoad = Load->getDest(); |
| 3367 | 3373 |
| 3368 // TODO(jvoung): handled folding opportunities. Sign and zero extension can | 3374 // TODO(jvoung): handled folding opportunities. Sign and zero extension can |
| 3369 // be folded into a load. | 3375 // be folded into a load. |
| 3370 InstAssign *Assign = InstAssign::create(Func, DestLoad, Src0); | 3376 InstAssign *Assign = InstAssign::create(Func, DestLoad, Src0); |
| 3371 lowerAssign(Assign); | 3377 lowerAssign(Assign); |
| 3372 } | 3378 } |
| 3373 | 3379 |
| 3374 void TargetARM32::doAddressOptLoad() {} | 3380 namespace { |
| 3381 void dumpAddressOpt(const Cfg *Func, const Variable *Base, int32_t Offset, | |
| 3382 const Variable *OffsetReg, int16_t OffsetRegShamt, | |
|
Jim Stichnoth
2015/11/08 20:05:51
tiny nit - OffsetRegShAmt ?
(here and below)
John
2015/11/09 22:42:24
Done.
| |
| 3383 const Inst *Reason) { | |
| 3384 if (!BuildDefs::dump()) | |
| 3385 return; | |
| 3386 if (!Func->isVerbose(IceV_AddrOpt)) | |
| 3387 return; | |
| 3388 OstreamLocker _(Func->getContext()); | |
| 3389 Ostream &Str = Func->getContext()->getStrDump(); | |
| 3390 Str << "Instruction: "; | |
| 3391 Reason->dumpDecorated(Func); | |
| 3392 Str << " results in Base="; | |
| 3393 if (Base) | |
| 3394 Base->dump(Func); | |
| 3395 else | |
| 3396 Str << "<null>"; | |
| 3397 Str << ", OffsetReg="; | |
| 3398 if (OffsetReg) | |
| 3399 OffsetReg->dump(Func); | |
| 3400 else | |
| 3401 Str << "<null>"; | |
| 3402 Str << ", Shift=" << OffsetRegShamt << ", Offset=" << Offset << "\n"; | |
| 3403 } | |
| 3404 | |
| 3405 bool matchAssign(const VariablesMetadata *VMetadata, Variable **Var, | |
| 3406 int32_t *Offset, const Inst **Reason) { | |
| 3407 // Var originates from Var=SrcVar ==> set Var:=SrcVar | |
| 3408 if (*Var == nullptr) | |
| 3409 return false; | |
| 3410 const Inst *VarAssign = VMetadata->getSingleDefinition(*Var); | |
| 3411 if (!VarAssign) | |
| 3412 return false; | |
| 3413 assert(!VMetadata->isMultiDef(*Var)); | |
| 3414 if (!llvm::isa<InstAssign>(VarAssign)) | |
| 3415 return false; | |
| 3416 | |
| 3417 Operand *SrcOp = VarAssign->getSrc(0); | |
| 3418 assert(SrcOp); | |
|
Jim Stichnoth
2015/11/08 20:05:50
This assert is a relic of the early days when mayb
John
2015/11/09 22:42:25
Done.
| |
| 3419 if (auto *SrcVar = llvm::dyn_cast<Variable>(SrcOp)) { | |
| 3420 if (!VMetadata->isMultiDef(SrcVar) || | |
| 3421 // TODO: ensure SrcVar stays single-BB | |
| 3422 false) { | |
| 3423 return false; | |
| 3424 *Var = SrcVar; | |
|
Jim Stichnoth
2015/11/08 20:05:51
this statement is unreachable
John
2015/11/09 22:42:24
Done.
| |
| 3425 } else if (auto *Const = llvm::dyn_cast<ConstantInteger32>(SrcOp)) { | |
|
Jim Stichnoth
2015/11/08 20:05:51
Don't use "else if" if the previous branch is real
John
2015/11/09 22:42:24
N/A.
| |
| 3426 if (false) | |
| 3427 return false; | |
|
Jim Stichnoth
2015/11/08 20:05:51
Why not return true?
:)
John
2015/11/09 22:42:24
I really don't know how high I was here.
| |
| 3428 int32_t MoreOffset = Const->getValue(); | |
| 3429 int32_t NewOffset = MoreOffset + *Offset; | |
| 3430 if (Utils::WouldOverflowAdd(*Offset, MoreOffset)) | |
| 3431 return false; | |
| 3432 *Var = nullptr; | |
| 3433 *Offset += NewOffset; | |
| 3434 } | |
| 3435 | |
| 3436 *Reason = VarAssign; | |
| 3437 return true; | |
| 3438 } | |
| 3439 | |
| 3440 return false; | |
| 3441 } | |
| 3442 | |
| 3443 bool isAddOrSub(const Inst *Inst, InstArithmetic::OpKind *Kind) { | |
| 3444 if (auto *Arith = llvm::dyn_cast_or_null<const InstArithmetic>(Inst)) { | |
|
Jim Stichnoth
2015/11/08 20:05:51
Can you just use dyn_cast? It looks like Inst can
John
2015/11/09 22:42:24
Done.
| |
| 3445 switch (Arith->getOp()) { | |
| 3446 default: | |
| 3447 return false; | |
| 3448 case InstArithmetic::Add: | |
| 3449 case InstArithmetic::Sub: | |
| 3450 *Kind = Arith->getOp(); | |
| 3451 return true; | |
| 3452 } | |
| 3453 } | |
| 3454 return false; | |
| 3455 } | |
| 3456 | |
| 3457 bool matchCombinedBaseIndex(const VariablesMetadata *VMetadata, Variable **Base, | |
| 3458 Variable **OffsetReg, int32_t OffsetRegShamt, | |
| 3459 const Inst **Reason) { | |
| 3460 // OffsetReg==nullptr && Base is Base=Var1+Var2 ==> | |
| 3461 // set Base=Var1, OffsetReg=Var2, Shift=0 | |
| 3462 if (*Base == nullptr) | |
| 3463 return false; | |
| 3464 if (*OffsetReg != nullptr) | |
| 3465 return false; | |
| 3466 assert(OffsetRegShamt == 0); | |
| 3467 const Inst *BaseInst = VMetadata->getSingleDefinition(*Base); | |
| 3468 if (BaseInst == nullptr) | |
| 3469 return false; | |
| 3470 assert(!VMetadata->isMultiDef(*Base)); | |
| 3471 if (BaseInst->getSrcSize() < 2) | |
| 3472 return false; | |
| 3473 auto *Var1 = llvm::dyn_cast<Variable>(BaseInst->getSrc(0)); | |
| 3474 if (!Var1) | |
| 3475 return false; | |
| 3476 if (VMetadata->isMultiDef(Var1)) | |
| 3477 return false; | |
| 3478 auto *Var2 = llvm::dyn_cast<Variable>(BaseInst->getSrc(1)); | |
| 3479 if (!Var2) | |
| 3480 return false; | |
| 3481 if (VMetadata->isMultiDef(Var2)) | |
| 3482 return false; | |
| 3483 InstArithmetic::OpKind _; | |
| 3484 if (!isAddOrSub(BaseInst, &_) || | |
| 3485 // TODO: ensure Var1 and Var2 stay single-BB | |
| 3486 false) | |
| 3487 return false; | |
| 3488 *Base = Var1; | |
| 3489 *OffsetReg = Var2; | |
| 3490 // OffsetRegShamt is already 0. | |
| 3491 *Reason = BaseInst; | |
| 3492 return true; | |
| 3493 } | |
| 3494 | |
| 3495 bool matchShiftedOffsetReg(const VariablesMetadata *VMetadata, | |
| 3496 Variable **OffsetReg, OperandARM32::ShiftKind *Kind, | |
| 3497 int32_t *OffsetRegShamt, const Inst **Reason) { | |
| 3498 // OffsetReg is OffsetReg=Var*Const && log2(Const)+Shift<=32 ==> | |
| 3499 // OffsetReg=Var, Shift+=log2(Const) | |
| 3500 // OffsetReg is OffsetReg=Var<<Const && Const+Shift<=32 ==> | |
| 3501 // OffsetReg=Var, Shift+=Const | |
| 3502 // OffsetReg is OffsetReg=Var>>Const && Const-Shift>=-32 ==> | |
| 3503 // OffsetReg=Var, Shift-=Const | |
| 3504 OperandARM32::ShiftKind NewShiftKind = OperandARM32::kNoShift; | |
| 3505 if (*OffsetReg == nullptr) | |
| 3506 return false; | |
| 3507 auto *IndexInst = VMetadata->getSingleDefinition(*OffsetReg); | |
| 3508 if (IndexInst == nullptr) | |
| 3509 return false; | |
| 3510 assert(!VMetadata->isMultiDef(*OffsetReg)); | |
| 3511 if (IndexInst->getSrcSize() < 2) | |
| 3512 return false; | |
| 3513 auto *ArithInst = llvm::dyn_cast<InstArithmetic>(IndexInst); | |
| 3514 if (ArithInst == nullptr) | |
| 3515 return false; | |
| 3516 auto *Var = llvm::dyn_cast<Variable>(ArithInst->getSrc(0)); | |
| 3517 if (Var == nullptr) | |
| 3518 return false; | |
| 3519 auto *Const = llvm::dyn_cast<ConstantInteger32>(ArithInst->getSrc(1)); | |
| 3520 if (Const == nullptr) { | |
| 3521 assert(!llvm::isa<ConstantInteger32>(ArithInst->getSrc(0))); | |
| 3522 return false; | |
| 3523 } | |
| 3524 if (VMetadata->isMultiDef(Var) || Const->getType() != IceType_i32) | |
| 3525 return false; | |
| 3526 | |
| 3527 uint32_t NewShamt = -1; | |
| 3528 switch (ArithInst->getOp()) { | |
| 3529 default: | |
| 3530 return false; | |
| 3531 case InstArithmetic::Shl: { | |
| 3532 NewShiftKind = OperandARM32::LSL; | |
| 3533 NewShamt = Const->getValue(); | |
| 3534 if (NewShamt > 31) | |
| 3535 return false; | |
| 3536 } break; | |
| 3537 case InstArithmetic::Lshr: { | |
| 3538 NewShiftKind = OperandARM32::LSR; | |
| 3539 NewShamt = Const->getValue(); | |
| 3540 if (NewShamt > 31) | |
| 3541 return false; | |
| 3542 } break; | |
| 3543 case InstArithmetic::Ashr: { | |
| 3544 NewShiftKind = OperandARM32::ASR; | |
| 3545 NewShamt = Const->getValue(); | |
| 3546 if (NewShamt > 31) | |
| 3547 return false; | |
| 3548 } break; | |
| 3549 case InstArithmetic::Udiv: | |
| 3550 case InstArithmetic::Mul: { | |
| 3551 const uint32_t UnsignedConst = Const->getValue(); | |
| 3552 NewShamt = llvm::findFirstSet(UnsignedConst); | |
| 3553 if (NewShamt != llvm::findLastSet(UnsignedConst)) { | |
| 3554 // First bit set is not the same as the last bit set, so Const is not | |
| 3555 // a power of 2. | |
| 3556 return false; | |
| 3557 } | |
| 3558 NewShiftKind = ArithInst->getOp() == InstArithmetic::Udiv | |
| 3559 ? OperandARM32::LSR | |
| 3560 : OperandARM32::LSL; | |
| 3561 } break; | |
| 3562 } | |
| 3563 // Allowed "transitions": | |
| 3564 // kNoShift -> * iff NewShamt < 31 | |
| 3565 // LSL -> LSL iff NewShamt + OffsetRegShamt < 31 | |
| 3566 // LSR -> LSR iff NewShamt + OffsetRegShamt < 31 | |
| 3567 // ASR -> ASR iff NewShamt + OffsetRegShamt < 31 | |
| 3568 if (*Kind != OperandARM32::kNoShift && *Kind != NewShiftKind) { | |
| 3569 return false; | |
| 3570 } | |
| 3571 const int32_t NewOffsetRegShamt = *OffsetRegShamt + NewShamt; | |
| 3572 if (NewOffsetRegShamt > 31) | |
| 3573 return false; | |
| 3574 *OffsetReg = Var; | |
| 3575 *OffsetRegShamt = NewOffsetRegShamt; | |
| 3576 *Kind = NewShiftKind; | |
| 3577 *Reason = IndexInst; | |
| 3578 return true; | |
| 3579 } | |
| 3580 | |
| 3581 bool matchOffsetBase(const VariablesMetadata *VMetadata, Variable **Base, | |
| 3582 int32_t *Offset, const Inst **Reason) { | |
| 3583 // Base is Base=Var+Const || Base is Base=Const+Var ==> | |
| 3584 // set Base=Var, Offset+=Const | |
| 3585 // Base is Base=Var-Const ==> | |
| 3586 // set Base=Var, Offset-=Const | |
| 3587 if (*Base == nullptr) | |
| 3588 return false; | |
| 3589 const Inst *BaseInst = VMetadata->getSingleDefinition(*Base); | |
| 3590 if (BaseInst == nullptr) { | |
| 3591 return false; | |
| 3592 } | |
| 3593 assert(!VMetadata->isMultiDef(*Base)); | |
| 3594 | |
| 3595 auto *ArithInst = llvm::dyn_cast<const InstArithmetic>(BaseInst); | |
| 3596 if (ArithInst == nullptr) | |
| 3597 return false; | |
| 3598 InstArithmetic::OpKind Kind; | |
| 3599 if (!isAddOrSub(ArithInst, &Kind)) | |
| 3600 return false; | |
| 3601 bool IsAdd = Kind == InstArithmetic::Add; | |
| 3602 Operand *Src0 = ArithInst->getSrc(0); | |
| 3603 Operand *Src1 = ArithInst->getSrc(1); | |
| 3604 auto *Var0 = llvm::dyn_cast<Variable>(Src0); | |
| 3605 auto *Var1 = llvm::dyn_cast<Variable>(Src1); | |
| 3606 auto *Const0 = llvm::dyn_cast<ConstantInteger32>(Src0); | |
| 3607 auto *Const1 = llvm::dyn_cast<ConstantInteger32>(Src1); | |
| 3608 Variable *NewBase = nullptr; | |
| 3609 int32_t NewOffset = *Offset; | |
| 3610 | |
| 3611 if (Var0 == nullptr && Const0 == nullptr) { | |
| 3612 assert(llvm::isa<ConstantRelocatable>(Src0)); | |
| 3613 return false; | |
| 3614 } | |
| 3615 | |
| 3616 if (Var1 == nullptr && Const1 == nullptr) { | |
| 3617 assert(llvm::isa<ConstantRelocatable>(Src1)); | |
| 3618 return false; | |
| 3619 } | |
| 3620 | |
| 3621 if (Var0 && Var1) | |
| 3622 // TODO(jpp): merge base/index splitting into here. | |
| 3623 return false; | |
| 3624 if (!IsAdd && Var1) | |
| 3625 return false; | |
| 3626 if (Var0) | |
| 3627 NewBase = Var0; | |
| 3628 else if (Var1) | |
| 3629 NewBase = Var1; | |
| 3630 // Compute the updated constant offset. | |
| 3631 if (Const0) { | |
| 3632 int32_t MoreOffset = IsAdd ? Const0->getValue() : -Const0->getValue(); | |
| 3633 if (Utils::WouldOverflowAdd(NewOffset, MoreOffset)) | |
| 3634 return false; | |
| 3635 NewOffset += MoreOffset; | |
| 3636 } | |
| 3637 if (Const1) { | |
| 3638 int32_t MoreOffset = IsAdd ? Const1->getValue() : -Const1->getValue(); | |
| 3639 if (Utils::WouldOverflowAdd(NewOffset, MoreOffset)) | |
| 3640 return false; | |
| 3641 NewOffset += MoreOffset; | |
| 3642 } | |
| 3643 | |
| 3644 // Update the computed address parameters once we are sure optimization | |
| 3645 // is valid. | |
| 3646 *Base = NewBase; | |
| 3647 *Offset = NewOffset; | |
| 3648 *Reason = BaseInst; | |
| 3649 return true; | |
| 3650 } | |
| 3651 } // end of anonymous namespace | |
| 3652 | |
| 3653 // ARM32 address modes: | |
| 3654 // ld/st i[8|16|32]: [reg], [reg +/- imm12], [pc +/- imm12], | |
| 3655 // [reg +/- reg << shamt5] | |
| 3656 // ld/st f[32|64] : [reg], [reg +/- imm8] , [pc +/- imm8] | |
| 3657 // ld/st vectors : [reg] | |
| 3658 // | |
| 3659 // For now, we don't handle address modes with Relocatables. | |
| 3660 namespace { | |
| 3661 // MemTraits contains per-type valid address mode information. | |
| 3662 #define X(tag, elementty, int_width, vec_width, sbits, ubits, rraddr, shaddr) \ | |
| 3663 static_assert(!(shaddr) || rraddr, "Check ICETYPEARM32_TABLE::" #tag); | |
| 3664 ICETYPEARM32_TABLE | |
| 3665 #undef X | |
| 3666 | |
| 3667 static const struct { | |
| 3668 int32_t ValidImmMask; | |
| 3669 bool CanHaveImm; | |
| 3670 bool CanHaveIndex; | |
| 3671 bool CanHaveShiftedIndex; | |
| 3672 } MemTraits[] = { | |
| 3673 #define X(tag, elementty, int_width, vec_width, sbits, ubits, rraddr, shaddr) \ | |
| 3674 { (1 << ubits) - 1, (ubits) > 0, rraddr, shaddr, } \ | |
| 3675 , | |
| 3676 ICETYPEARM32_TABLE | |
| 3677 #undef X | |
| 3678 }; | |
| 3679 static constexpr SizeT MemTraitsSize = llvm::array_lengthof(MemTraits); | |
| 3680 } // end of anonymous namespace | |
| 3681 | |
| 3682 OperandARM32Mem *TargetARM32::formAddressingMode(Type Ty, Cfg *Func, | |
| 3683 const Inst *LdSt, | |
| 3684 Operand *Base) { | |
| 3685 assert(Base != nullptr); | |
| 3686 int32_t OffsetImm = 0; | |
| 3687 Variable *OffsetReg = nullptr; | |
| 3688 int32_t OffsetRegShamt = 0; | |
| 3689 OperandARM32::ShiftKind ShiftKind = OperandARM32::kNoShift; | |
| 3690 | |
| 3691 Func->resetCurrentNode(); | |
| 3692 if (Func->isVerbose(IceV_AddrOpt)) { | |
| 3693 OstreamLocker L(Func->getContext()); | |
|
Jim Stichnoth
2015/11/08 20:05:51
Earlier you used "OstreamLocker _(...);"
John
2015/11/09 22:42:24
Changes everywhere.
| |
| 3694 Ostream &Str = Func->getContext()->getStrDump(); | |
| 3695 Str << "\nAddress mode formation:\t"; | |
| 3696 LdSt->dumpDecorated(Func); | |
| 3697 } | |
| 3698 | |
| 3699 if (isVectorType(Ty)) | |
| 3700 // vector loads and stores do not allow offsets, and only support the | |
| 3701 // "[reg]" addressing mode (the other supported modes are write back.) | |
| 3702 return nullptr; | |
| 3703 | |
| 3704 auto *BaseVar = llvm::dyn_cast<Variable>(Base); | |
| 3705 if (BaseVar == nullptr) | |
| 3706 return nullptr; | |
| 3707 | |
| 3708 assert(Ty < MemTraitsSize); | |
| 3709 auto *TypeTraits = &MemTraits[Ty]; | |
| 3710 const bool CanHaveIndex = TypeTraits->CanHaveIndex; | |
| 3711 const bool CanHaveShiftedIndex = TypeTraits->CanHaveShiftedIndex; | |
| 3712 const bool CanHaveImm = TypeTraits->CanHaveImm; | |
| 3713 const int32_t ValidImmMask = TypeTraits->ValidImmMask; | |
| 3714 assert(!CanHaveImm || ValidImmMask >= 0); | |
| 3715 | |
| 3716 const VariablesMetadata *VMetadata = Func->getVMetadata(); | |
| 3717 const Inst *Reason = nullptr; | |
| 3718 | |
| 3719 do { | |
| 3720 if (Reason != nullptr) { | |
| 3721 dumpAddressOpt(Func, BaseVar, OffsetImm, OffsetReg, OffsetRegShamt, | |
| 3722 Reason); | |
| 3723 Reason = nullptr; | |
| 3724 } | |
| 3725 | |
| 3726 if (matchAssign(VMetadata, &BaseVar, &OffsetImm, &Reason)) { | |
| 3727 continue; | |
| 3728 } | |
| 3729 | |
| 3730 if (CanHaveIndex && | |
| 3731 matchAssign(VMetadata, &OffsetReg, &OffsetImm, &Reason)) { | |
| 3732 continue; | |
| 3733 } | |
| 3734 | |
| 3735 if (CanHaveIndex && matchCombinedBaseIndex(VMetadata, &BaseVar, &OffsetReg, | |
| 3736 OffsetRegShamt, &Reason)) { | |
| 3737 continue; | |
| 3738 } | |
| 3739 | |
| 3740 if (CanHaveShiftedIndex) { | |
| 3741 if (matchShiftedOffsetReg(VMetadata, &OffsetReg, &ShiftKind, | |
| 3742 &OffsetRegShamt, &Reason)) { | |
| 3743 continue; | |
| 3744 } | |
| 3745 | |
| 3746 if ((OffsetRegShamt == 0) && | |
| 3747 matchShiftedOffsetReg(VMetadata, &BaseVar, &ShiftKind, | |
| 3748 &OffsetRegShamt, &Reason)) { | |
| 3749 std::swap(BaseVar, OffsetReg); | |
| 3750 continue; | |
| 3751 } | |
| 3752 } | |
| 3753 | |
| 3754 if (matchOffsetBase(VMetadata, &BaseVar, &OffsetImm, &Reason)) { | |
| 3755 continue; | |
| 3756 } | |
| 3757 } while (Reason); | |
| 3758 | |
| 3759 if (BaseVar == nullptr) { | |
| 3760 // [OffsetReg{, LSL Shamt}{, #OffsetImm}] is not legal in ARM, so we have to | |
| 3761 // legalize the addressing mode to [BaseReg, OffsetReg{, LSL Shamt}]. | |
| 3762 // Instead of a zeroed BaseReg, we initialize it with OffsetImm: | |
| 3763 // | |
| 3764 // [OffsetReg{, LSL Shamt}{, #OffsetImm}] -> | |
| 3765 // mov BaseReg, #OffsetImm | |
| 3766 // use of [BaseReg, OffsetReg{, LSL Shamt}] | |
| 3767 // | |
| 3768 static constexpr Type PointerType = IceType_i32; | |
| 3769 BaseVar = makeReg(PointerType); | |
| 3770 Context.insert( | |
| 3771 InstAssign::create(Func, BaseVar, Ctx->getConstantInt32(OffsetImm))); | |
| 3772 OffsetImm = 0; | |
| 3773 } else if (OffsetImm != 0) { | |
| 3774 // ARM Ldr/Str instructions have limited range immediates. The formation | |
| 3775 // loop above materialized an Immediate carelessly, so we ensure the | |
| 3776 // generated offset is sane. | |
| 3777 const int32_t PositiveOffset = OffsetImm > 0 ? OffsetImm : -OffsetImm; | |
| 3778 const InstArithmetic::OpKind Op = | |
| 3779 OffsetImm > 0 ? InstArithmetic::Add : InstArithmetic::Sub; | |
| 3780 | |
| 3781 if (!CanHaveImm || !isLegalMemOffset(Ty, OffsetImm) || | |
| 3782 OffsetReg != nullptr) { | |
| 3783 if (OffsetReg == nullptr) { | |
| 3784 // We formed a [Base, #const] addressing mode which is not encodable in | |
| 3785 // ARM. There is little point in forming an address mode now if we don't | |
| 3786 // have an offset. Effectively, we would end up with something like | |
| 3787 // | |
| 3788 // [Base, #const] -> add T, Base, #const | |
| 3789 // use of [T] | |
| 3790 // | |
| 3791 // Which is exactly what we already have. So we just bite the bullet | |
| 3792 // here and don't form any address mode. | |
| 3793 return nullptr; | |
| 3794 } | |
| 3795 // We formed [Base, Offset {, LSL Amnt}, #const]. Oops. Legalize it to | |
| 3796 // | |
| 3797 // [Base, Offset, {LSL amount}, #const] -> | |
| 3798 // add T, Base, #const | |
| 3799 // use of [T, Offset {, LSL amount}] | |
| 3800 static constexpr Type PointerType = IceType_i32; | |
|
Jim Stichnoth
2015/11/08 20:05:51
Can you use the existing getPointerType() instead?
John
2015/11/09 22:42:24
Then it is no longer a constexpr. :(
Jim Stichnoth
2015/11/09 22:47:49
Then why did you subsequently change it to "const"
John
2015/11/09 22:52:30
Done.
| |
| 3801 Variable *T = makeReg(PointerType); | |
| 3802 Context.insert(InstArithmetic::create( | |
| 3803 Func, Op, T, BaseVar, Ctx->getConstantInt32(PositiveOffset))); | |
| 3804 BaseVar = T; | |
| 3805 OffsetImm = 0; | |
| 3806 } | |
| 3807 } | |
| 3808 | |
| 3809 assert(BaseVar != nullptr); | |
| 3810 assert(OffsetImm == 0 || OffsetReg == nullptr); | |
| 3811 assert(OffsetReg == nullptr || CanHaveIndex); | |
| 3812 assert(OffsetImm < 0 ? (ValidImmMask & -OffsetImm) == -OffsetImm | |
| 3813 : (ValidImmMask & OffsetImm) == OffsetImm); | |
| 3814 | |
| 3815 if (OffsetReg != nullptr) { | |
| 3816 return OperandARM32Mem::create(Func, Ty, BaseVar, OffsetReg, ShiftKind, | |
| 3817 OffsetRegShamt); | |
| 3818 } | |
| 3819 | |
| 3820 return OperandARM32Mem::create( | |
| 3821 Func, Ty, BaseVar, | |
| 3822 llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(OffsetImm))); | |
| 3823 } | |
| 3824 | |
| 3825 void TargetARM32::doAddressOptLoad() { | |
| 3826 Inst *Instr = Context.getCur(); | |
| 3827 assert(llvm::isa<InstLoad>(Instr)); | |
| 3828 Variable *Dest = Instr->getDest(); | |
| 3829 Operand *Addr = Instr->getSrc(0); | |
| 3830 if (OperandARM32Mem *Mem = | |
| 3831 formAddressingMode(Dest->getType(), Func, Instr, Addr)) { | |
| 3832 Instr->setDeleted(); | |
| 3833 Context.insert(InstLoad::create(Func, Dest, Mem)); | |
| 3834 } | |
| 3835 } | |
| 3375 | 3836 |
| 3376 void TargetARM32::randomlyInsertNop(float Probability, | 3837 void TargetARM32::randomlyInsertNop(float Probability, |
| 3377 RandomNumberGenerator &RNG) { | 3838 RandomNumberGenerator &RNG) { |
| 3378 RandomNumberGeneratorWrapper RNGW(RNG); | 3839 RandomNumberGeneratorWrapper RNGW(RNG); |
| 3379 if (RNGW.getTrueWithProbability(Probability)) { | 3840 if (RNGW.getTrueWithProbability(Probability)) { |
| 3380 UnimplementedError(Func->getContext()->getFlags()); | 3841 UnimplementedError(Func->getContext()->getFlags()); |
| 3381 } | 3842 } |
| 3382 } | 3843 } |
| 3383 | 3844 |
| 3384 void TargetARM32::lowerPhi(const InstPhi * /*Inst*/) { | 3845 void TargetARM32::lowerPhi(const InstPhi * /*Inst*/) { |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3565 Variable *ValueHi = legalizeToReg(hiOperand(Value)); | 4026 Variable *ValueHi = legalizeToReg(hiOperand(Value)); |
| 3566 Variable *ValueLo = legalizeToReg(loOperand(Value)); | 4027 Variable *ValueLo = legalizeToReg(loOperand(Value)); |
| 3567 _str(ValueHi, llvm::cast<OperandARM32Mem>(hiOperand(NewAddr))); | 4028 _str(ValueHi, llvm::cast<OperandARM32Mem>(hiOperand(NewAddr))); |
| 3568 _str(ValueLo, llvm::cast<OperandARM32Mem>(loOperand(NewAddr))); | 4029 _str(ValueLo, llvm::cast<OperandARM32Mem>(loOperand(NewAddr))); |
| 3569 } else { | 4030 } else { |
| 3570 Variable *ValueR = legalizeToReg(Value); | 4031 Variable *ValueR = legalizeToReg(Value); |
| 3571 _str(ValueR, NewAddr); | 4032 _str(ValueR, NewAddr); |
| 3572 } | 4033 } |
| 3573 } | 4034 } |
| 3574 | 4035 |
| 3575 void TargetARM32::doAddressOptStore() {} | 4036 void TargetARM32::doAddressOptStore() { |
| 4037 Inst *Instr = Context.getCur(); | |
| 4038 assert(llvm::isa<InstStore>(Instr)); | |
| 4039 Operand *Src = Instr->getSrc(0); | |
| 4040 Operand *Addr = Instr->getSrc(1); | |
| 4041 if (OperandARM32Mem *Mem = | |
| 4042 formAddressingMode(Src->getType(), Func, Instr, Addr)) { | |
| 4043 Instr->setDeleted(); | |
| 4044 Context.insert(InstStore::create(Func, Src, Mem)); | |
| 4045 } | |
| 4046 } | |
| 3576 | 4047 |
| 3577 void TargetARM32::lowerSwitch(const InstSwitch *Inst) { | 4048 void TargetARM32::lowerSwitch(const InstSwitch *Inst) { |
| 3578 // This implements the most naive possible lowering. | 4049 // This implements the most naive possible lowering. |
| 3579 // cmp a,val[0]; jeq label[0]; cmp a,val[1]; jeq label[1]; ... jmp default | 4050 // cmp a,val[0]; jeq label[0]; cmp a,val[1]; jeq label[1]; ... jmp default |
| 3580 Operand *Src0 = Inst->getComparison(); | 4051 Operand *Src0 = Inst->getComparison(); |
| 3581 SizeT NumCases = Inst->getNumCases(); | 4052 SizeT NumCases = Inst->getNumCases(); |
| 3582 if (Src0->getType() == IceType_i64) { | 4053 if (Src0->getType() == IceType_i64) { |
| 3583 Src0 = legalizeUndef(Src0); | 4054 Src0 = legalizeUndef(Src0); |
| 3584 Variable *Src0Lo = legalizeToReg(loOperand(Src0)); | 4055 Variable *Src0Lo = legalizeToReg(loOperand(Src0)); |
| 3585 Variable *Src0Hi = legalizeToReg(hiOperand(Src0)); | 4056 Variable *Src0Hi = legalizeToReg(hiOperand(Src0)); |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3662 } | 4133 } |
| 3663 } | 4134 } |
| 3664 } | 4135 } |
| 3665 } | 4136 } |
| 3666 | 4137 |
| 3667 // Go through the various types of operands: OperandARM32Mem, | 4138 // Go through the various types of operands: OperandARM32Mem, |
| 3668 // OperandARM32Flex, Constant, and Variable. Given the above assertion, if | 4139 // OperandARM32Flex, Constant, and Variable. Given the above assertion, if |
| 3669 // type of operand is not legal (e.g., OperandARM32Mem and !Legal_Mem), we | 4140 // type of operand is not legal (e.g., OperandARM32Mem and !Legal_Mem), we |
| 3670 // can always copy to a register. | 4141 // can always copy to a register. |
| 3671 if (auto *Mem = llvm::dyn_cast<OperandARM32Mem>(From)) { | 4142 if (auto *Mem = llvm::dyn_cast<OperandARM32Mem>(From)) { |
| 3672 static const struct { | |
| 3673 bool CanHaveOffset; | |
| 3674 bool CanHaveIndex; | |
| 3675 } MemTraits[] = { | |
| 3676 #define X(tag, elementty, int_width, vec_width, sbits, ubits, rraddr) \ | |
| 3677 { (ubits) > 0, rraddr } \ | |
| 3678 , | |
| 3679 ICETYPEARM32_TABLE | |
| 3680 #undef X | |
| 3681 }; | |
| 3682 // Before doing anything with a Mem operand, we need to ensure that the | 4143 // Before doing anything with a Mem operand, we need to ensure that the |
| 3683 // Base and Index components are in physical registers. | 4144 // Base and Index components are in physical registers. |
| 3684 Variable *Base = Mem->getBase(); | 4145 Variable *Base = Mem->getBase(); |
| 3685 Variable *Index = Mem->getIndex(); | 4146 Variable *Index = Mem->getIndex(); |
| 3686 ConstantInteger32 *Offset = Mem->getOffset(); | 4147 ConstantInteger32 *Offset = Mem->getOffset(); |
| 3687 assert(Index == nullptr || Offset == nullptr); | 4148 assert(Index == nullptr || Offset == nullptr); |
| 3688 Variable *RegBase = nullptr; | 4149 Variable *RegBase = nullptr; |
| 3689 Variable *RegIndex = nullptr; | 4150 Variable *RegIndex = nullptr; |
| 3690 if (Base) { | 4151 assert(Base); |
| 3691 RegBase = legalizeToReg(Base); | 4152 RegBase = legalizeToReg(Base); |
| 3692 } | 4153 bool InvalidImm = false; |
| 4154 assert(Ty < MemTraitsSize); | |
| 3693 if (Index) { | 4155 if (Index) { |
| 4156 assert(Offset == nullptr); | |
| 4157 assert(MemTraits[Ty].CanHaveIndex); | |
| 3694 RegIndex = legalizeToReg(Index); | 4158 RegIndex = legalizeToReg(Index); |
| 3695 if (!MemTraits[Ty].CanHaveIndex) { | |
| 3696 Variable *T = makeReg(IceType_i32, getReservedTmpReg()); | |
| 3697 _add(T, RegBase, RegIndex); | |
| 3698 RegBase = T; | |
| 3699 RegIndex = nullptr; | |
| 3700 } | |
| 3701 } | 4159 } |
| 3702 if (Offset && Offset->getValue() != 0) { | 4160 if (Offset && Offset->getValue() != 0) { |
| 3703 static constexpr bool SignExt = false; | 4161 assert(Index == nullptr); |
| 3704 if (!MemTraits[Ty].CanHaveOffset || | 4162 static constexpr bool ZeroExt = false; |
| 3705 !OperandARM32Mem::canHoldOffset(Ty, SignExt, Offset->getValue())) { | 4163 assert(MemTraits[Ty].CanHaveImm); |
| 3706 Variable *T = legalizeToReg(Offset, getReservedTmpReg()); | 4164 if (!OperandARM32Mem::canHoldOffset(Ty, ZeroExt, Offset->getValue())) { |
| 3707 _add(T, T, RegBase); | 4165 assert(RegBase->hasReg() && |
|
Jim Stichnoth
2015/11/08 20:05:51
Two separate asserts for better precision when it
John
2015/11/09 22:42:24
Done.
| |
| 3708 RegBase = T; | 4166 RegBase->getRegNum() == (int)getFrameOrStackReg()); |
| 3709 Offset = llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(0)); | 4167 // We are a bit more lenient with invalid immediate when accessing the |
| 4168 // stack here, and then rely on legalizeStackSlots() to fix things as | |
| 4169 // appropriate. | |
| 4170 InvalidImm = true; | |
| 3710 } | 4171 } |
| 3711 } | 4172 } |
| 3712 | 4173 |
| 3713 // Create a new operand if there was a change. | 4174 // Create a new operand if there was a change. |
| 3714 if (Base != RegBase || Index != RegIndex) { | 4175 if (Base != RegBase || Index != RegIndex) { |
| 3715 // There is only a reg +/- reg or reg + imm form. | 4176 // There is only a reg +/- reg or reg + imm form. |
| 3716 // Figure out which to re-create. | 4177 // Figure out which to re-create. |
| 3717 if (RegBase && RegIndex) { | 4178 if (RegIndex) { |
| 3718 Mem = OperandARM32Mem::create(Func, Ty, RegBase, RegIndex, | 4179 Mem = OperandARM32Mem::create(Func, Ty, RegBase, RegIndex, |
| 3719 Mem->getShiftOp(), Mem->getShiftAmt(), | 4180 Mem->getShiftOp(), Mem->getShiftAmt(), |
| 3720 Mem->getAddrMode()); | 4181 Mem->getAddrMode()); |
| 3721 } else { | 4182 } else { |
| 3722 Mem = OperandARM32Mem::create(Func, Ty, RegBase, Offset, | 4183 Mem = OperandARM32Mem::create(Func, Ty, RegBase, Offset, |
| 3723 Mem->getAddrMode()); | 4184 Mem->getAddrMode()); |
| 3724 } | 4185 } |
| 3725 } | 4186 } |
| 3726 if (Allowed & Legal_Mem) { | 4187 if (Allowed & Legal_Mem) { |
| 3727 From = Mem; | 4188 From = Mem; |
| 3728 } else { | 4189 } else { |
| 3729 Variable *Reg = makeReg(Ty, RegNum); | 4190 Variable *Reg = makeReg(Ty, RegNum); |
| 3730 _ldr(Reg, Mem); | 4191 if (InvalidImm) { |
| 4192 // If Mem has an invalid immediate, we legalize it to a Reg using mov | |
| 4193 // instead of ldr because legalizeStackSlots() will later kick in and | |
| 4194 // fix the immediate for us. | |
| 4195 _mov(Reg, Mem); | |
| 4196 } else { | |
| 4197 _ldr(Reg, Mem); | |
| 4198 } | |
| 4199 | |
| 3731 From = Reg; | 4200 From = Reg; |
| 3732 } | 4201 } |
| 3733 return From; | 4202 return From; |
| 3734 } | 4203 } |
| 3735 | 4204 |
| 3736 if (auto *Flex = llvm::dyn_cast<OperandARM32Flex>(From)) { | 4205 if (auto *Flex = llvm::dyn_cast<OperandARM32Flex>(From)) { |
| 3737 if (!(Allowed & Legal_Flex)) { | 4206 if (!(Allowed & Legal_Flex)) { |
| 3738 if (auto *FlexReg = llvm::dyn_cast<OperandARM32FlexReg>(Flex)) { | 4207 if (auto *FlexReg = llvm::dyn_cast<OperandARM32FlexReg>(Flex)) { |
| 3739 if (FlexReg->getShiftOp() == OperandARM32::kNoShift) { | 4208 if (FlexReg->getShiftOp() == OperandARM32::kNoShift) { |
| 3740 From = FlexReg->getReg(); | 4209 From = FlexReg->getReg(); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3793 Variable *Reg = makeReg(Ty, RegNum); | 4262 Variable *Reg = makeReg(Ty, RegNum); |
| 3794 _movw(Reg, C); | 4263 _movw(Reg, C); |
| 3795 _movt(Reg, C); | 4264 _movt(Reg, C); |
| 3796 return Reg; | 4265 return Reg; |
| 3797 } else { | 4266 } else { |
| 3798 assert(isScalarFloatingType(Ty)); | 4267 assert(isScalarFloatingType(Ty)); |
| 3799 // Load floats/doubles from literal pool. | 4268 // Load floats/doubles from literal pool. |
| 3800 // TODO(jvoung): Allow certain immediates to be encoded directly in an | 4269 // TODO(jvoung): Allow certain immediates to be encoded directly in an |
| 3801 // operand. See Table A7-18 of the ARM manual: "Floating-point modified | 4270 // operand. See Table A7-18 of the ARM manual: "Floating-point modified |
| 3802 // immediate constants". Or, for 32-bit floating point numbers, just | 4271 // immediate constants". Or, for 32-bit floating point numbers, just |
| 3803 // encode the raw bits into a movw/movt pair to GPR, and vmov to an SREG, | 4272 // encode the raw bits into a movw/movt pair to GPR, and vmov to an SREG |
| 3804 // instead of using a movw/movt pair to get the const-pool address then | 4273 // instead of using a movw/movt pair to get the const-pool address then |
| 3805 // loading to SREG. | 4274 // loading to SREG. |
| 3806 std::string Buffer; | 4275 std::string Buffer; |
| 3807 llvm::raw_string_ostream StrBuf(Buffer); | 4276 llvm::raw_string_ostream StrBuf(Buffer); |
| 3808 llvm::cast<Constant>(From)->emitPoolLabel(StrBuf, Ctx); | 4277 llvm::cast<Constant>(From)->emitPoolLabel(StrBuf, Ctx); |
| 3809 llvm::cast<Constant>(From)->setShouldBePooled(true); | 4278 llvm::cast<Constant>(From)->setShouldBePooled(true); |
| 3810 Constant *Offset = Ctx->getConstantSym(0, StrBuf.str(), true); | 4279 Constant *Offset = Ctx->getConstantSym(0, StrBuf.str(), true); |
| 3811 Variable *BaseReg = makeReg(getPointerType()); | 4280 Variable *BaseReg = makeReg(getPointerType()); |
| 3812 _movw(BaseReg, Offset); | 4281 _movw(BaseReg, Offset); |
| 3813 _movt(BaseReg, Offset); | 4282 _movt(BaseReg, Offset); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3863 } | 4332 } |
| 3864 | 4333 |
| 3865 OperandARM32Mem *TargetARM32::formMemoryOperand(Operand *Operand, Type Ty) { | 4334 OperandARM32Mem *TargetARM32::formMemoryOperand(Operand *Operand, Type Ty) { |
| 3866 OperandARM32Mem *Mem = llvm::dyn_cast<OperandARM32Mem>(Operand); | 4335 OperandARM32Mem *Mem = llvm::dyn_cast<OperandARM32Mem>(Operand); |
| 3867 // It may be the case that address mode optimization already creates an | 4336 // It may be the case that address mode optimization already creates an |
| 3868 // OperandARM32Mem, so in that case it wouldn't need another level of | 4337 // OperandARM32Mem, so in that case it wouldn't need another level of |
| 3869 // transformation. | 4338 // transformation. |
| 3870 if (Mem) { | 4339 if (Mem) { |
| 3871 return llvm::cast<OperandARM32Mem>(legalize(Mem)); | 4340 return llvm::cast<OperandARM32Mem>(legalize(Mem)); |
| 3872 } | 4341 } |
| 3873 // If we didn't do address mode optimization, then we only have a base/offset | 4342 // If we didn't do address mode optimization, then we only have a |
| 3874 // to work with. ARM always requires a base register, so just use that to | 4343 // base/offset to work with. ARM always requires a base register, so |
| 3875 // hold the operand. | 4344 // just use that to hold the operand. |
| 3876 Variable *Base = legalizeToReg(Operand); | 4345 Variable *Base = legalizeToReg(Operand); |
| 3877 return OperandARM32Mem::create( | 4346 return OperandARM32Mem::create( |
| 3878 Func, Ty, Base, | 4347 Func, Ty, Base, |
| 3879 llvm::cast<ConstantInteger32>(Ctx->getConstantZero(IceType_i32))); | 4348 llvm::cast<ConstantInteger32>(Ctx->getConstantZero(IceType_i32))); |
| 3880 } | 4349 } |
| 3881 | 4350 |
| 3882 Variable64On32 *TargetARM32::makeI64RegPair() { | 4351 Variable64On32 *TargetARM32::makeI64RegPair() { |
| 3883 Variable64On32 *Reg = | 4352 Variable64On32 *Reg = |
| 3884 llvm::cast<Variable64On32>(Func->makeVariable(IceType_i64)); | 4353 llvm::cast<Variable64On32>(Func->makeVariable(IceType_i64)); |
| 3885 Reg->setMustHaveReg(); | 4354 Reg->setMustHaveReg(); |
| (...skipping 380 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4266 // Technically R9 is used for TLS with Sandboxing, and we reserve it. | 4735 // Technically R9 is used for TLS with Sandboxing, and we reserve it. |
| 4267 // However, for compatibility with current NaCl LLVM, don't claim that. | 4736 // However, for compatibility with current NaCl LLVM, don't claim that. |
| 4268 Str << ".eabi_attribute 14, 3 @ Tag_ABI_PCS_R9_use: Not used\n"; | 4737 Str << ".eabi_attribute 14, 3 @ Tag_ABI_PCS_R9_use: Not used\n"; |
| 4269 } | 4738 } |
| 4270 | 4739 |
| 4271 llvm::SmallBitVector TargetARM32::TypeToRegisterSet[IceType_NUM]; | 4740 llvm::SmallBitVector TargetARM32::TypeToRegisterSet[IceType_NUM]; |
| 4272 llvm::SmallBitVector TargetARM32::RegisterAliases[RegARM32::Reg_NUM]; | 4741 llvm::SmallBitVector TargetARM32::RegisterAliases[RegARM32::Reg_NUM]; |
| 4273 llvm::SmallBitVector TargetARM32::ScratchRegs; | 4742 llvm::SmallBitVector TargetARM32::ScratchRegs; |
| 4274 | 4743 |
| 4275 } // end of namespace Ice | 4744 } // end of namespace Ice |
| OLD | NEW |