//*************************************************************************** // File: // SparcInstrInfo.cpp // // Purpose: // // History: // 10/15/01 - Vikram Adve - Created //**************************************************************************/ #include "SparcInternals.h" #include "SparcInstrSelectionSupport.h" #include "llvm/Target/Sparc.h" #include "llvm/CodeGen/InstrSelection.h" #include "llvm/CodeGen/InstrSelectionSupport.h" #include "llvm/CodeGen/MachineCodeForMethod.h" #include "llvm/CodeGen/MachineCodeForInstruction.h" #include "llvm/Function.h" #include "llvm/BasicBlock.h" #include "llvm/Instruction.h" #include "llvm/Constants.h" #include "llvm/DerivedTypes.h" using std::vector; //************************ Internal Functions ******************************/ static const uint32_t MAXLO = (1 << 10) - 1; // set bits set by %lo(*) static const uint32_t MAXSIMM = (1 << 12) - 1; // set bits in simm13 field of OR // Set a 32-bit unsigned constant in the register `dest'. // static inline void CreateSETUWConst(const TargetMachine& target, uint32_t C, Instruction* dest, std::vector& mvec) { MachineInstr *miSETHI = NULL, *miOR = NULL; // In order to get efficient code, we should not generate the SETHI if // all high bits are 1 (i.e., this is a small signed value that fits in // the simm13 field of OR). So we check for and handle that case specially. // NOTE: The value C = 0x80000000 is bad: sC < 0 *and* -sC < 0. // In fact, sC == -sC, so we have to check for this explicitly. int32_t sC = (int32_t) C; bool smallSignedValue = sC < 0 && sC != -sC && -sC < (int32_t) MAXSIMM; // Set the high 22 bits in dest if non-zero and simm13 field of OR not enough if (!smallSignedValue && (C & ~MAXLO) && C > MAXSIMM) { miSETHI = Create2OperandInstr_UImmed(SETHI, C, dest); miSETHI->setOperandHi32(0); mvec.push_back(miSETHI); } // Set the low 10 or 12 bits in dest. This is necessary if no SETHI // was generated, or if the low 10 bits are non-zero. if (miSETHI==NULL || C & MAXLO) { if (miSETHI) { // unsigned value with high-order bits set using SETHI miOR = Create3OperandInstr_UImmed(OR, dest, C, dest); miOR->setOperandLo32(1); } else { // unsigned or small signed value that fits in simm13 field of OR assert(smallSignedValue || (C & ~MAXSIMM) == 0); miOR = new MachineInstr(OR); miOR->SetMachineOperandReg(0, target.getRegInfo().getZeroRegNum()); miOR->SetMachineOperandConst(1, MachineOperand::MO_SignExtendedImmed, sC); miOR->SetMachineOperandVal(2,MachineOperand::MO_VirtualRegister,dest); } mvec.push_back(miOR); } assert((miSETHI || miOR) && "Oops, no code was generated!"); } // Set a 32-bit constant (given by a symbolic label) in the register `dest'. // Not needed for SPARC v9 but useful to make the two SETX functions similar static inline void CreateSETUWLabel(const TargetMachine& target, Value* val, Instruction* dest, std::vector& mvec) { MachineInstr* MI; // Set the high 22 bits in dest MI = Create2OperandInstr(SETHI, val, dest); MI->setOperandHi32(0); mvec.push_back(MI); // Set the low 10 bits in dest MI = Create3OperandInstr(OR, dest, val, dest); MI->setOperandLo32(1); mvec.push_back(MI); } // Set a 32-bit signed constant in the register `dest', // with sign-extension to 64 bits. static inline void CreateSETSWConst(const TargetMachine& target, int32_t C, Instruction* dest, std::vector& mvec) { MachineInstr* MI; // Set the low 32 bits of dest CreateSETUWConst(target, (uint32_t) C, dest, mvec); // Sign-extend to the high 32 bits if needed if (C < 0 && (-C) > (int32_t) MAXSIMM) { MI = Create3OperandInstr_UImmed(SRA, dest, 0, dest); mvec.push_back(MI); } } // Set a 64-bit signed or unsigned constant in the register `dest'. static inline void CreateSETXConst(const TargetMachine& target, uint64_t C, Instruction* tmpReg, Instruction* dest, std::vector& mvec) { assert(C > (unsigned int) ~0 && "Use SETUW/SETSW for 32-bit values!"); MachineInstr* MI; // Code to set the upper 32 bits of the value in register `tmpReg' CreateSETUWConst(target, (C >> 32), tmpReg, mvec); // Shift tmpReg left by 32 bits MI = Create3OperandInstr_UImmed(SLLX, tmpReg, 32, tmpReg); mvec.push_back(MI); // Code to set the low 32 bits of the value in register `dest' CreateSETUWConst(target, C, dest, mvec); // dest = OR(tmpReg, dest) MI = Create3OperandInstr(OR, dest, tmpReg, dest); mvec.push_back(MI); } // Set a 64-bit constant (given by a symbolic label) in the register `dest'. static inline void CreateSETXLabel(const TargetMachine& target, Value* val, Instruction* tmpReg, Instruction* dest, std::vector& mvec) { assert(isa(val) || isa(val) && "I only know about constant values and global addresses"); MachineInstr* MI; MI = Create2OperandInstr_Addr(SETHI, val, tmpReg); MI->setOperandHi64(0); mvec.push_back(MI); MI = Create3OperandInstr_Addr(OR, tmpReg, val, tmpReg); MI->setOperandLo64(1); mvec.push_back(MI); MI = Create3OperandInstr_UImmed(SLLX, tmpReg, 32, tmpReg); mvec.push_back(MI); MI = Create2OperandInstr_Addr(SETHI, val, dest); MI->setOperandHi32(0); mvec.push_back(MI); MI = Create3OperandInstr(OR, dest, tmpReg, dest); mvec.push_back(MI); MI = Create3OperandInstr_Addr(OR, dest, val, dest); MI->setOperandLo32(1); mvec.push_back(MI); } static inline void CreateIntSetInstruction(const TargetMachine& target, int64_t C, Instruction* dest, std::vector& mvec, MachineCodeForInstruction& mcfi) { assert(dest->getType()->isSigned() && "Use CreateUIntSetInstruction()"); uint64_t absC = (C >= 0)? C : -C; if (absC > (uint32_t) ~0) { // C does not fit in 32 bits TmpInstruction* tmpReg = new TmpInstruction(Type::IntTy); mcfi.addTemp(tmpReg); CreateSETXConst(target, (uint64_t) C, tmpReg, dest, mvec); } else CreateSETSWConst(target, (int32_t) C, dest, mvec); } static inline void CreateUIntSetInstruction(const TargetMachine& target, uint64_t C, Instruction* dest, std::vector& mvec, MachineCodeForInstruction& mcfi) { assert(! dest->getType()->isSigned() && "Use CreateIntSetInstruction()"); MachineInstr* M; if (C > (uint32_t) ~0) { // C does not fit in 32 bits assert(dest->getType() == Type::ULongTy && "Sign extension problems"); TmpInstruction *tmpReg = new TmpInstruction(Type::IntTy); mcfi.addTemp(tmpReg); CreateSETXConst(target, C, tmpReg, dest, mvec); } else CreateSETUWConst(target, C, dest, mvec); } //************************* External Classes *******************************/ //--------------------------------------------------------------------------- // class UltraSparcInstrInfo // // Purpose: // Information about individual instructions. // Most information is stored in the SparcMachineInstrDesc array above. // Other information is computed on demand, and most such functions // default to member functions in base class MachineInstrInfo. //--------------------------------------------------------------------------- /*ctor*/ UltraSparcInstrInfo::UltraSparcInstrInfo(const TargetMachine& tgt) : MachineInstrInfo(tgt, SparcMachineInstrDesc, /*descSize = */ NUM_TOTAL_OPCODES, /*numRealOpCodes = */ NUM_REAL_OPCODES) { } // // Create an instruction sequence to put the constant `val' into // the virtual register `dest'. `val' may be a Constant or a // GlobalValue, viz., the constant address of a global variable or function. // The generated instructions are returned in `mvec'. // Any temp. registers (TmpInstruction) created are recorded in mcfi. // Any stack space required is allocated via MachineCodeForMethod. // void UltraSparcInstrInfo::CreateCodeToLoadConst(const TargetMachine& target, Function* F, Value* val, Instruction* dest, std::vector& mvec, MachineCodeForInstruction& mcfi) const { assert(isa(val) || isa(val) && "I only know about constant values and global addresses"); // Use a "set" instruction for known constants or symbolic constants (labels) // that can go in an integer reg. // We have to use a "load" instruction for all other constants, // in particular, floating point constants. // const Type* valType = val->getType(); if (isa(val) || valType->isIntegral() || valType == Type::BoolTy) { if (isa(val)) { TmpInstruction* tmpReg = new TmpInstruction(PointerType::get(val->getType()), val); mcfi.addTemp(tmpReg); CreateSETXLabel(target, val, tmpReg, dest, mvec); } else if (! dest->getType()->isSigned()) { bool isValidConstant; uint64_t C = GetConstantValueAsUnsignedInt(val, isValidConstant); assert(isValidConstant && "Unrecognized constant"); CreateUIntSetInstruction(target, C, dest, mvec, mcfi); } else { bool isValidConstant; int64_t C = GetConstantValueAsSignedInt(val, isValidConstant); assert(isValidConstant && "Unrecognized constant"); CreateIntSetInstruction(target, C, dest, mvec, mcfi); } } else { // Make an instruction sequence to load the constant, viz: // SETX , tmpReg, addrReg // LOAD /*addr*/ addrReg, /*offset*/ 0, dest // First, create a tmp register to be used by the SETX sequence. TmpInstruction* tmpReg = new TmpInstruction(PointerType::get(val->getType()), val); mcfi.addTemp(tmpReg); // Create another TmpInstruction for the address register TmpInstruction* addrReg = new TmpInstruction(PointerType::get(val->getType()), val); mcfi.addTemp(addrReg); // Put the address (a symbolic name) into a register CreateSETXLabel(target, val, tmpReg, addrReg, mvec); // Generate the load instruction int64_t zeroOffset = 0; // to avoid ambiguity with (Value*) 0 MachineInstr* MI = Create3OperandInstr_SImmed(ChooseLoadInstruction(val->getType()), addrReg, zeroOffset, dest); mvec.push_back(MI); // Make sure constant is emitted to constant pool in assembly code. MachineCodeForMethod::get(F).addToConstantPool(cast(val)); } } // Create an instruction sequence to copy an integer value `val' // to a floating point value `dest' by copying to memory and back. // val must be an integral type. dest must be a Float or Double. // The generated instructions are returned in `mvec'. // Any temp. registers (TmpInstruction) created are recorded in mcfi. // Any stack space required is allocated via MachineCodeForMethod. // void UltraSparcInstrInfo::CreateCodeToCopyIntToFloat(const TargetMachine& target, Function* F, Value* val, Instruction* dest, std::vector& mvec, MachineCodeForInstruction& mcfi) const { assert((val->getType()->isIntegral() || isa(val->getType())) && "Source type must be integral"); assert(dest->getType()->isFloatingPoint() && "Dest type must be float/double"); int offset = MachineCodeForMethod::get(F).allocateLocalVar(target, val); // Store instruction stores `val' to [%fp+offset]. // The store and load opCodes are based on the value being copied, and // they use integer and float types that accomodate the // larger of the source type and the destination type: // On SparcV9: int for float, long for double. // Note that the store instruction is the same for signed and unsigned ints. Type* tmpType = (dest->getType() == Type::FloatTy)? Type::IntTy : Type::LongTy; MachineInstr* store = new MachineInstr(ChooseStoreInstruction(tmpType)); store->SetMachineOperandVal(0, MachineOperand::MO_VirtualRegister, val); store->SetMachineOperandReg(1, target.getRegInfo().getFramePointer()); store->SetMachineOperandConst(2,MachineOperand::MO_SignExtendedImmed,offset); mvec.push_back(store); // Load instruction loads [%fp+offset] to `dest'. // MachineInstr* load =new MachineInstr(ChooseLoadInstruction(dest->getType())); load->SetMachineOperandReg(0, target.getRegInfo().getFramePointer()); load->SetMachineOperandConst(1, MachineOperand::MO_SignExtendedImmed,offset); load->SetMachineOperandVal(2, MachineOperand::MO_VirtualRegister, dest); mvec.push_back(load); } // Similarly, create an instruction sequence to copy an FP value // `val' to an integer value `dest' by copying to memory and back. // The generated instructions are returned in `mvec'. // Any temp. registers (TmpInstruction) created are recorded in mcfi. // Any stack space required is allocated via MachineCodeForMethod. // void UltraSparcInstrInfo::CreateCodeToCopyFloatToInt(const TargetMachine& target, Function* F, Value* val, Instruction* dest, std::vector& mvec, MachineCodeForInstruction& mcfi) const { const Type* opTy = val->getType(); const Type* destTy = dest->getType(); assert(opTy->isFloatingPoint() && "Source type must be float/double"); assert((destTy->isIntegral() || isa(destTy)) && "Dest type must be integral"); int offset = MachineCodeForMethod::get(F).allocateLocalVar(target, val); // Store instruction stores `val' to [%fp+offset]. // The store opCode is based only the source value being copied. // MachineInstr* store=new MachineInstr(ChooseStoreInstruction(val->getType())); store->SetMachineOperandVal(0, MachineOperand::MO_VirtualRegister, val); store->SetMachineOperandReg(1, target.getRegInfo().getFramePointer()); store->SetMachineOperandConst(2,MachineOperand::MO_SignExtendedImmed,offset); mvec.push_back(store); // Load instruction loads [%fp+offset] to `dest'. // The type of the load opCode is the integer type that matches the // source type in size: (and the dest type in sign): // On SparcV9: int for float, long for double. // Note that we *must* use signed loads even for unsigned dest types, to // ensure that we get the right sign-extension for smaller-than-64-bit // unsigned dest. types (i.e., UByte, UShort or UInt): const Type* loadTy = opTy == Type::FloatTy? Type::IntTy : Type::LongTy; MachineInstr* load = new MachineInstr(ChooseLoadInstruction(loadTy)); load->SetMachineOperandReg(0, target.getRegInfo().getFramePointer()); load->SetMachineOperandConst(1, MachineOperand::MO_SignExtendedImmed,offset); load->SetMachineOperandVal(2, MachineOperand::MO_VirtualRegister, dest); mvec.push_back(load); } // Create instruction(s) to copy src to dest, for arbitrary types // The generated instructions are returned in `mvec'. // Any temp. registers (TmpInstruction) created are recorded in mcfi. // Any stack space required is allocated via MachineCodeForMethod. // void UltraSparcInstrInfo::CreateCopyInstructionsByType(const TargetMachine& target, Function *F, Value* src, Instruction* dest, vector& mvec, MachineCodeForInstruction& mcfi) const { bool loadConstantToReg = false; const Type* resultType = dest->getType(); MachineOpCode opCode = ChooseAddInstructionByType(resultType); if (opCode == INVALID_OPCODE) { assert(0 && "Unsupported result type in CreateCopyInstructionsByType()"); return; } // if `src' is a constant that doesn't fit in the immed field or if it is // a global variable (i.e., a constant address), generate a load // instruction instead of an add // if (isa(src)) { unsigned int machineRegNum; int64_t immedValue; MachineOperand::MachineOperandType opType = ChooseRegOrImmed(src, opCode, target, /*canUseImmed*/ true, machineRegNum, immedValue); if (opType == MachineOperand::MO_VirtualRegister) loadConstantToReg = true; } else if (isa(src)) loadConstantToReg = true; if (loadConstantToReg) { // `src' is constant and cannot fit in immed field for the ADD // Insert instructions to "load" the constant into a register target.getInstrInfo().CreateCodeToLoadConst(target, F, src, dest, mvec, mcfi); } else { // Create an add-with-0 instruction of the appropriate type. // Make `src' the second operand, in case it is a constant // Use (unsigned long) 0 for a NULL pointer value. // const Type* zeroValueType = isa(resultType) ? Type::ULongTy : resultType; MachineInstr* minstr = Create3OperandInstr(opCode, Constant::getNullValue(zeroValueType), src, dest); mvec.push_back(minstr); } } // Create instruction sequence to produce a sign-extended register value // from an arbitrary sized value (sized in bits, not bytes). // For SPARC v9, we sign-extend the given unsigned operand using SLL; SRA. // The generated instructions are returned in `mvec'. // Any temp. registers (TmpInstruction) created are recorded in mcfi. // Any stack space required is allocated via MachineCodeForMethod. // void UltraSparcInstrInfo::CreateSignExtensionInstructions( const TargetMachine& target, Function* F, Value* unsignedSrcVal, unsigned int srcSizeInBits, Value* dest, vector& mvec, MachineCodeForInstruction& mcfi) const { MachineInstr* M; assert(srcSizeInBits < 64 && "Sign extension unnecessary!"); assert(srcSizeInBits > 0 && srcSizeInBits <= 32 && "Hmmm... 32 < srcSizeInBits < 64 unexpected but could be handled here."); if (srcSizeInBits < 32) { // SLL is needed since operand size is < 32 bits. TmpInstruction *tmpI = new TmpInstruction(dest->getType(), unsignedSrcVal, dest,"make32"); mcfi.addTemp(tmpI); M = Create3OperandInstr_UImmed(SLL,unsignedSrcVal,32-srcSizeInBits,tmpI); mvec.push_back(M); unsignedSrcVal = tmpI; } M = Create3OperandInstr_UImmed(SRA, unsignedSrcVal, 32-srcSizeInBits, dest); mvec.push_back(M); }