Commit 7cb2db32 by Karl Schimpf

Handle MOV (immediate) and MOVT to load ARM global addresses.

Adds a new type of fixup to handle the relocatable fixups needed for movw and movt on a global addresses. Also adds movw and movt methods to the ARM assembler. Also makes ARM register names visible (without a target lowering object), so that the ARM integrated assembler can generate the appropriate assembly. Note that the integrated assembler needs to generate the corresponding movw/movt, and follows the instruction with the bytes that appear in the corresponding assembler buffer. This allows the ability to test if we have generated the correct values, and will be set up properly for ELF emission. BUG= https://code.google.com/p/nativeclient/issues/detail?id=4334 R=stichnot@chromium.org Review URL: https://codereview.chromium.org/1424863005 .
parent 3b8a15ee
...@@ -330,6 +330,8 @@ void Assembler::clz(Register rd, Register rm, Condition cond) { ...@@ -330,6 +330,8 @@ void Assembler::clz(Register rd, Register rm, Condition cond) {
} }
#if
// Moved to ARM::AssemblerARM32::movw
void Assembler::movw(Register rd, uint16_t imm16, Condition cond) { void Assembler::movw(Register rd, uint16_t imm16, Condition cond) {
ASSERT(cond != kNoCondition); ASSERT(cond != kNoCondition);
int32_t encoding = static_cast<int32_t>(cond) << kConditionShift | int32_t encoding = static_cast<int32_t>(cond) << kConditionShift |
...@@ -339,6 +341,7 @@ void Assembler::movw(Register rd, uint16_t imm16, Condition cond) { ...@@ -339,6 +341,7 @@ void Assembler::movw(Register rd, uint16_t imm16, Condition cond) {
} }
// Moved to ARM::AssemblerARM32::movt
void Assembler::movt(Register rd, uint16_t imm16, Condition cond) { void Assembler::movt(Register rd, uint16_t imm16, Condition cond) {
ASSERT(cond != kNoCondition); ASSERT(cond != kNoCondition);
int32_t encoding = static_cast<int32_t>(cond) << kConditionShift | int32_t encoding = static_cast<int32_t>(cond) << kConditionShift |
...@@ -346,6 +349,7 @@ void Assembler::movt(Register rd, uint16_t imm16, Condition cond) { ...@@ -346,6 +349,7 @@ void Assembler::movt(Register rd, uint16_t imm16, Condition cond) {
static_cast<int32_t>(rd) << kRdShift | (imm16 & 0xfff); static_cast<int32_t>(rd) << kRdShift | (imm16 & 0xfff);
Emit(encoding); Emit(encoding);
} }
#endif
void Assembler::EmitMulOp(Condition cond, int32_t opcode, void Assembler::EmitMulOp(Condition cond, int32_t opcode,
......
...@@ -1069,10 +1069,14 @@ class Assembler : public ValueObject { ...@@ -1069,10 +1069,14 @@ class Assembler : public ValueObject {
bool use_far_branches_; bool use_far_branches_;
#if 0
// If you are thinking of using one or both of these instructions directly, // If you are thinking of using one or both of these instructions directly,
// instead LoadImmediate should probably be used. // instead LoadImmediate should probably be used.
// Moved to ARM::AssemblerARM32::movw
void movw(Register rd, uint16_t imm16, Condition cond = AL); void movw(Register rd, uint16_t imm16, Condition cond = AL);
// Moved to ARM::AssemblerARM32::movt
void movt(Register rd, uint16_t imm16, Condition cond = AL); void movt(Register rd, uint16_t imm16, Condition cond = AL);
#endif
void BindARMv6(Label* label); void BindARMv6(Label* label);
void BindARMv7(Label* label); void BindARMv7(Label* label);
......
...@@ -38,7 +38,9 @@ static constexpr IValueT B4 = 1 << 4; ...@@ -38,7 +38,9 @@ static constexpr IValueT B4 = 1 << 4;
static constexpr IValueT B5 = 1 << 5; static constexpr IValueT B5 = 1 << 5;
static constexpr IValueT B6 = 1 << 6; static constexpr IValueT B6 = 1 << 6;
static constexpr IValueT B21 = 1 << 21; static constexpr IValueT B21 = 1 << 21;
static constexpr IValueT B22 = 1 << 22;
static constexpr IValueT B24 = 1 << 24; static constexpr IValueT B24 = 1 << 24;
static constexpr IValueT B25 = 1 << 25;
// Constants used for the decoding or encoding of the individual fields of // Constants used for the decoding or encoding of the individual fields of
// instructions. Based on ARM section A5.1. // instructions. Based on ARM section A5.1.
...@@ -222,6 +224,32 @@ bool canEncodeBranchOffset(IOffsetT Offset) { ...@@ -222,6 +224,32 @@ bool canEncodeBranchOffset(IOffsetT Offset) {
namespace Ice { namespace Ice {
namespace ARM32 { namespace ARM32 {
size_t MoveRelocatableFixup::emit(GlobalContext *Ctx,
const Assembler &Asm) const {
static constexpr const size_t FixupSize = sizeof(IValueT);
if (!BuildDefs::dump())
return FixupSize;
Ostream &Str = Ctx->getStrEmit();
IValueT Inst = Asm.load<IValueT>(position());
Str << "\tmov" << (kind() == llvm::ELF::R_ARM_MOVW_ABS_NC ? "w" : "t") << "\t"
<< RegARM32::RegNames[(Inst >> kRdShift) & 0xF]
<< ", #:" << (kind() == llvm::ELF::R_ARM_MOVW_ABS_NC ? "lower" : "upper")
<< "16:" << symbol(Ctx) << "\t@ .word "
<< llvm::format_hex_no_prefix(Inst, 8) << "\n";
return FixupSize;
}
MoveRelocatableFixup *AssemblerARM32::createMoveFixup(bool IsMovW,
const Constant *Value) {
MoveRelocatableFixup *F =
new (allocate<MoveRelocatableFixup>()) MoveRelocatableFixup();
F->set_kind(IsMovW ? llvm::ELF::R_ARM_MOVW_ABS_NC
: llvm::ELF::R_ARM_MOVT_ABS);
F->set_value(Value);
Buffer.installFixup(F);
return F;
}
void AssemblerARM32::bindCfgNodeLabel(const CfgNode *Node) { void AssemblerARM32::bindCfgNodeLabel(const CfgNode *Node) {
GlobalContext *Ctx = Node->getCfg()->getContext(); GlobalContext *Ctx = Node->getCfg()->getContext();
if (BuildDefs::dump() && !Ctx->getFlags().getDisableHybridAssembly()) { if (BuildDefs::dump() && !Ctx->getFlags().getDisableHybridAssembly()) {
...@@ -517,9 +545,8 @@ void AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc, ...@@ -517,9 +545,8 @@ void AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc,
// MOV (immediate) - ARM section A8.8.102, encoding A1: // MOV (immediate) - ARM section A8.8.102, encoding A1:
// mov{S}<c> <Rd>, #<RotatedImm8> // mov{S}<c> <Rd>, #<RotatedImm8>
// //
// cccc0011101s0000ddddiiiiiiiiiiii where cccc=Cond, s=SetFlags, dddd=Rd, // cccc0011101s0000ddddiiiiiiiiiiii where cccc=Cond, s=SetFlags, dddd=Rd, and
// and iiiiiiiiiiii=RotatedImm8=Src. Note: We don't use movs in this // iiiiiiiiiiii=RotatedImm8=Src. Note: We don't use movs in this assembler.
// assembler.
constexpr bool SetFlags = false; constexpr bool SetFlags = false;
if ((Rd == RegARM32::Encoded_Reg_pc && SetFlags)) if ((Rd == RegARM32::Encoded_Reg_pc && SetFlags))
// Conditions of rule violated. // Conditions of rule violated.
...@@ -529,6 +556,62 @@ void AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc, ...@@ -529,6 +556,62 @@ void AssemblerARM32::mov(const Operand *OpRd, const Operand *OpSrc,
emitType01(Cond, kInstTypeDataImmediate, Mov, SetFlags, Rn, Rd, Src); emitType01(Cond, kInstTypeDataImmediate, Mov, SetFlags, Rn, Rd, Src);
} }
void AssemblerARM32::movw(const Operand *OpRd, const Operand *OpSrc,
CondARM32::Cond Cond) {
IValueT Rd;
if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
return setNeedsTextFixup();
auto *Src = llvm::dyn_cast<ConstantRelocatable>(OpSrc);
if (Src == nullptr)
return setNeedsTextFixup();
// MOV (immediate) - ARM section A8.8.102, encoding A2:
// movw<c> <Rd>, #<imm16>
//
// cccc00110000iiiiddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, and
// iiiiiiiiiiiiiiii=imm16.
if (!isConditionDefined(Cond))
// Conditions of rule violated.
return setNeedsTextFixup();
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
// Use 0 for the lower 16 bits of the relocatable, and add a fixup to
// install the correct bits.
constexpr bool IsMovW = true;
emitFixup(createMoveFixup(IsMovW, Src));
constexpr IValueT Imm16 = 0;
const IValueT Encoding = encodeCondition(Cond) << kConditionShift | B25 |
B24 | ((Imm16 >> 12) << 16) | Rd << kRdShift |
(Imm16 & 0xfff);
emitInst(Encoding);
}
void AssemblerARM32::movt(const Operand *OpRd, const Operand *OpSrc,
CondARM32::Cond Cond) {
IValueT Rd;
if (decodeOperand(OpRd, Rd) != DecodedAsRegister)
return setNeedsTextFixup();
auto *Src = llvm::dyn_cast<ConstantRelocatable>(OpSrc);
if (Src == nullptr)
return setNeedsTextFixup();
// MOVT - ARM section A8.8.102, encoding A2:
// movt<c> <Rd>, #<imm16>
//
// cccc00110100iiiiddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, and
// iiiiiiiiiiiiiiii=imm16.
if (!isConditionDefined(Cond))
// Conditions of rule violated.
return setNeedsTextFixup();
AssemblerBuffer::EnsureCapacity ensured(&Buffer);
// Use 0 for the lower 16 bits of the relocatable, and add a fixup to
// install the correct bits.
constexpr bool IsMovW = false;
emitFixup(createMoveFixup(IsMovW, Src));
constexpr IValueT Imm16 = 0;
const IValueT Encoding = encodeCondition(Cond) << kConditionShift | B25 |
B24 | B22 | ((Imm16 >> 12) << 16) | Rd << kRdShift |
(Imm16 & 0xfff);
emitInst(Encoding);
}
void AssemblerARM32::sbc(const Operand *OpRd, const Operand *OpRn, void AssemblerARM32::sbc(const Operand *OpRd, const Operand *OpRn,
const Operand *OpSrc1, bool SetFlags, const Operand *OpSrc1, bool SetFlags,
CondARM32::Cond Cond) { CondARM32::Cond Cond) {
......
...@@ -44,6 +44,16 @@ using IValueT = uint32_t; ...@@ -44,6 +44,16 @@ using IValueT = uint32_t;
/// An Offset value (+/-) used in an ARM 32-bit instruction. /// An Offset value (+/-) used in an ARM 32-bit instruction.
using IOffsetT = int32_t; using IOffsetT = int32_t;
/// Handles encoding of bottom/top 16 bits of an address using movw/movt.
class MoveRelocatableFixup : public AssemblerFixup {
MoveRelocatableFixup &operator=(const MoveRelocatableFixup &) = delete;
MoveRelocatableFixup(const MoveRelocatableFixup &) = default;
public:
MoveRelocatableFixup() = default;
size_t emit(GlobalContext *Ctx, const Assembler &Asm) const override;
};
class AssemblerARM32 : public Assembler { class AssemblerARM32 : public Assembler {
AssemblerARM32(const AssemblerARM32 &) = delete; AssemblerARM32(const AssemblerARM32 &) = delete;
AssemblerARM32 &operator=(const AssemblerARM32 &) = delete; AssemblerARM32 &operator=(const AssemblerARM32 &) = delete;
...@@ -65,6 +75,8 @@ public: ...@@ -65,6 +75,8 @@ public:
} }
} }
MoveRelocatableFixup *createMoveFixup(bool IsMovW, const Constant *Value);
void alignFunction() override { void alignFunction() override {
const SizeT Align = 1 << getBundleAlignLog2Bytes(); const SizeT Align = 1 << getBundleAlignLog2Bytes();
SizeT BytesNeeded = Utils::OffsetToAlignment(Buffer.getPosition(), Align); SizeT BytesNeeded = Utils::OffsetToAlignment(Buffer.getPosition(), Align);
...@@ -137,6 +149,10 @@ public: ...@@ -137,6 +149,10 @@ public:
void mov(const Operand *OpRd, const Operand *OpSrc, CondARM32::Cond Cond); void mov(const Operand *OpRd, const Operand *OpSrc, CondARM32::Cond Cond);
void movw(const Operand *OpRd, const Operand *OpSrc, CondARM32::Cond Cond);
void movt(const Operand *OpRd, const Operand *OpSrc, CondARM32::Cond Cond);
void bx(RegARM32::GPRRegister Rm, CondARM32::Cond Cond = CondARM32::AL); void bx(RegARM32::GPRRegister Rm, CondARM32::Cond Cond = CondARM32::AL);
void sbc(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, void sbc(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1,
......
...@@ -797,7 +797,7 @@ void InstARM32Call::emit(const Cfg *Func) const { ...@@ -797,7 +797,7 @@ void InstARM32Call::emit(const Cfg *Func) const {
// This shouldn't happen (typically have to copy the full 32-bits to a // This shouldn't happen (typically have to copy the full 32-bits to a
// register and do an indirect jump). // register and do an indirect jump).
llvm::report_fatal_error("ARM32Call to ConstantInteger32"); llvm::report_fatal_error("ARM32Call to ConstantInteger32");
} else if (const auto CallTarget = } else if (const auto *CallTarget =
llvm::dyn_cast<ConstantRelocatable>(getCallTarget())) { llvm::dyn_cast<ConstantRelocatable>(getCallTarget())) {
// Calls only have 24-bits, but the linker should insert veneers to extend // Calls only have 24-bits, but the linker should insert veneers to extend
// the range if needed. // the range if needed.
...@@ -889,6 +889,11 @@ void InstARM32TwoAddrGPR<K>::emitIAS(const Cfg *Func) const { ...@@ -889,6 +889,11 @@ void InstARM32TwoAddrGPR<K>::emitIAS(const Cfg *Func) const {
emitUsingTextFixup(Func); emitUsingTextFixup(Func);
} }
template <InstARM32::InstKindARM32 K, bool Nws>
void InstARM32UnaryopGPR<K, Nws>::emitIAS(const Cfg *Func) const {
emitUsingTextFixup(Func);
}
template <> void InstARM32Movw::emit(const Cfg *Func) const { template <> void InstARM32Movw::emit(const Cfg *Func) const {
if (!BuildDefs::dump()) if (!BuildDefs::dump())
return; return;
...@@ -906,6 +911,14 @@ template <> void InstARM32Movw::emit(const Cfg *Func) const { ...@@ -906,6 +911,14 @@ template <> void InstARM32Movw::emit(const Cfg *Func) const {
} }
} }
template <> void InstARM32Movw::emitIAS(const Cfg *Func) const {
assert(getSrcSize() == 1);
ARM32::AssemblerARM32 *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
Asm->movw(getDest(), getSrc(0), getPredicate());
if (Asm->needsTextFixup())
emitUsingTextFixup(Func);
}
template <> void InstARM32Movt::emit(const Cfg *Func) const { template <> void InstARM32Movt::emit(const Cfg *Func) const {
if (!BuildDefs::dump()) if (!BuildDefs::dump())
return; return;
...@@ -924,6 +937,14 @@ template <> void InstARM32Movt::emit(const Cfg *Func) const { ...@@ -924,6 +937,14 @@ template <> void InstARM32Movt::emit(const Cfg *Func) const {
} }
} }
template <> void InstARM32Movt::emitIAS(const Cfg *Func) const {
assert(getSrcSize() == 2);
ARM32::AssemblerARM32 *Asm = Func->getAssembler<ARM32::AssemblerARM32>();
Asm->movt(getDest(), getSrc(1), getPredicate());
if (Asm->needsTextFixup())
emitUsingTextFixup(Func);
}
void InstARM32Pop::emit(const Cfg *Func) const { void InstARM32Pop::emit(const Cfg *Func) const {
// TODO(jpp): Improve FP register save/restore. // TODO(jpp): Improve FP register save/restore.
if (!BuildDefs::dump()) if (!BuildDefs::dump())
...@@ -1462,4 +1483,13 @@ template class InstARM32ThreeAddrFP<InstARM32::Vsub>; ...@@ -1462,4 +1483,13 @@ template class InstARM32ThreeAddrFP<InstARM32::Vsub>;
template class InstARM32TwoAddrGPR<InstARM32::Movt>; template class InstARM32TwoAddrGPR<InstARM32::Movt>;
template class InstARM32UnaryopGPR<InstARM32::Movw, false>;
template class InstARM32UnaryopGPR<InstARM32::Clz, false>;
template class InstARM32UnaryopGPR<InstARM32::Mvn, false>;
template class InstARM32UnaryopGPR<InstARM32::Rbit, false>;
template class InstARM32UnaryopGPR<InstARM32::Rev, false>;
template class InstARM32UnaryopGPR<InstARM32::Sxt, true>;
template class InstARM32UnaryopGPR<InstARM32::Uxt, true>;
template class InstARM32UnaryopFP<InstARM32::Vsqrt>;
} // end of namespace Ice } // end of namespace Ice
...@@ -418,6 +418,7 @@ public: ...@@ -418,6 +418,7 @@ public:
return; return;
emitUnaryopGPR(Opcode, this, Func, NeedsWidthSuffix); emitUnaryopGPR(Opcode, this, Func, NeedsWidthSuffix);
} }
void emitIAS(const Cfg *Func) const override;
void dump(const Cfg *Func) const override { void dump(const Cfg *Func) const override {
if (!BuildDefs::dump()) if (!BuildDefs::dump())
return; return;
......
...@@ -114,6 +114,8 @@ public: ...@@ -114,6 +114,8 @@ public:
assert(RegNum <= Reg_QREG_Last); assert(RegNum <= Reg_QREG_Last);
return QRegister(RegNum - Reg_QREG_First); return QRegister(RegNum - Reg_QREG_First);
} }
static const char *RegNames[];
}; };
} // end of namespace Ice } // end of namespace Ice
......
...@@ -386,18 +386,18 @@ bool TargetARM32::doBranchOpt(Inst *I, const CfgNode *NextNode) { ...@@ -386,18 +386,18 @@ bool TargetARM32::doBranchOpt(Inst *I, const CfgNode *NextNode) {
return false; return false;
} }
IceString TargetARM32::getRegName(SizeT RegNum, Type Ty) const { const char *RegARM32::RegNames[] = {
assert(RegNum < RegARM32::Reg_NUM);
(void)Ty;
static const char *RegNames[] = {
#define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \ #define X(val, encode, name, scratch, preserved, stackptr, frameptr, isInt, \
isI64Pair, isFP32, isFP64, isVec128, alias_init) \ isI64Pair, isFP32, isFP64, isVec128, alias_init) \
name, name,
REGARM32_TABLE REGARM32_TABLE
#undef X #undef X
}; };
return RegNames[RegNum]; IceString TargetARM32::getRegName(SizeT RegNum, Type Ty) const {
assert(RegNum < RegARM32::Reg_NUM);
(void)Ty;
return RegARM32::RegNames[RegNum];
} }
Variable *TargetARM32::getPhysicalRegister(SizeT RegNum, Type Ty) { Variable *TargetARM32::getPhysicalRegister(SizeT RegNum, Type Ty) {
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
; RUN: %p2i --filetype=iasm -i %s --target=arm32 --assemble --disassemble \ ; RUN: %p2i --filetype=iasm -i %s --target=arm32 --assemble --disassemble \
; RUN: --args -O2 | FileCheck %s --check-prefix=DIS ; RUN: --args -O2 | FileCheck %s --check-prefix=DIS
@filler = internal global [128 x i8] zeroinitializer, align 4
@global1 = internal global [4 x i8] zeroinitializer, align 4 @global1 = internal global [4 x i8] zeroinitializer, align 4
; ASM-LABEL: global1: ; ASM-LABEL: global1:
...@@ -53,8 +55,8 @@ define internal i32 @load() { ...@@ -53,8 +55,8 @@ define internal i32 @load() {
; IASM-LABEL:load: ; IASM-LABEL:load:
; IASM-NEXT: .Lload$__0: ; IASM-NEXT: .Lload$__0:
; IASM-NEXT: movw r0, #:lower16:global1 ; IASM-NEXT: movw r0, #:lower16:global1 @ .word e3000000
; IASM-NEXT: movt r0, #:upper16:global1 ; IASM-NEXT: movt r0, #:upper16:global1 @ .word e3400000
; IASM-NEXT: ldr r0, [r0] ; IASM-NEXT: ldr r0, [r0]
; IASM-NEXT: .byte 0x1e ; IASM-NEXT: .byte 0x1e
; IASM-NEXT: .byte 0xff ; IASM-NEXT: .byte 0xff
...@@ -82,8 +84,8 @@ define internal void @store(i32 %v) { ...@@ -82,8 +84,8 @@ define internal void @store(i32 %v) {
; IASM-LABEL:store: ; IASM-LABEL:store:
; IASM-NEXT: .Lstore$__0: ; IASM-NEXT: .Lstore$__0:
; IASM-NEXT: movw r1, #:lower16:global1 ; IASM-NEXT: movw r1, #:lower16:global1 @ .word e3001000
; IASM-NEXT: movt r1, #:upper16:global1 ; IASM-NEXT: movt r1, #:upper16:global1 @ .word e3401000
; IASM-NEXT: str r0, [r1] ; IASM-NEXT: str r0, [r1]
; IASM-NEXT: .byte 0x1e ; IASM-NEXT: .byte 0x1e
; IASM-NEXT: .byte 0xff ; IASM-NEXT: .byte 0xff
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment