Index: include/llvm/MC/MCStreamer.h =================================================================== --- include/llvm/MC/MCStreamer.h +++ include/llvm/MC/MCStreamer.h @@ -86,6 +86,27 @@ virtual void finish(); }; +class AArch64TargetStreamer : public MCTargetStreamer { +public: + AArch64TargetStreamer(MCStreamer &S); + ~AArch64TargetStreamer(); + + + void finish() override; + + /// Callback used to implement the ldr= pseudo. + /// Add a new entry to the constant pool for the current section and return an + /// MCExpr that can be used to refer to the constant pool location. + const MCExpr *addConstantPoolEntry(const MCExpr *); + + /// Callback used to implemnt the .ltorg directive. + /// Emit contents of constant pool for the current section. + void emitCurrentConstantPool(); + +private: + std::unique_ptr ConstantPools; +}; + // FIXME: declared here because it is used from // lib/CodeGen/AsmPrinter/ARMException.cpp. class ARMTargetStreamer : public MCTargetStreamer { Index: lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp =================================================================== --- lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -43,6 +43,11 @@ MCSubtargetInfo &STI; MCAsmParser &Parser; + AArch64TargetStreamer &getTargetStreamer() { + MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); + return static_cast(TS); + } + MCAsmParser &getParser() const { return Parser; } MCAsmLexer &getLexer() const { return Parser.getLexer(); } @@ -67,6 +72,7 @@ bool parseDirectiveTLSDescCall(SMLoc L); bool parseDirectiveLOH(StringRef LOH, SMLoc L); + bool parseDirectiveLtorg(SMLoc L); bool validateInstruction(MCInst &Inst, SmallVectorImpl &Loc); bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, @@ -105,6 +111,8 @@ const MCTargetOptions &Options) : MCTargetAsmParser(), STI(_STI), Parser(_Parser) { MCAsmParserExtension::Initialize(_Parser); + if (Parser.getStreamer().getTargetStreamer() == nullptr) + new AArch64TargetStreamer(Parser.getStreamer()); // Initialize the set of available features. setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); @@ -3004,6 +3012,43 @@ Operands.push_back(AArch64Operand::CreateImm(ImmVal, S, E, getContext())); return false; } + case AsmToken::Equal: { + SMLoc Loc = Parser.getTok().getLoc(); + if (Mnemonic != "ldr") // only parse for ldr pseudo (e.g. ldr r0, =val) + return Error(Loc, "unexpected token in operand"); + Parser.Lex(); // Eat '=' + const MCExpr *SubExprVal; + if (getParser().parseExpression(SubExprVal)) + return true; + + MCContext& Ctx = getContext(); + E = SMLoc::getFromPointer(Loc.getPointer() - 1); + // If the op is an imm and can be fit into a mov, then replace ldr with mov. + if (isa(SubExprVal) && Operands.size() >= 2 && + static_cast(*Operands[1]).isReg()) { + bool IsXReg = AArch64MCRegisterClasses[AArch64::GPR64allRegClassID].contains( + Operands[1]->getReg()); + uint64_t Imm = (dyn_cast(SubExprVal))->getValue(); + uint32_t ShiftAmt = 0, MaxShiftAmt = IsXReg ? 48 : 16; + while(Imm > 0xFFFF && countTrailingZeros(Imm) >= 16) { + ShiftAmt += 16; + Imm >>= 16; + } + if (ShiftAmt <= MaxShiftAmt && Imm <= 0xFFFF) { + Operands[0] = AArch64Operand::CreateToken("movz", false, Loc, Ctx); + Operands.push_back(AArch64Operand::CreateImm( + MCConstantExpr::Create(Imm, Ctx), S, E, Ctx)); + if (ShiftAmt) + Operands.push_back(AArch64Operand::CreateShiftExtend(AArch64_AM::LSL, + ShiftAmt, true, S, E, Ctx)); + return false; + } + } + // If it is a label or an imm that cannot fit in a movz, put it into CP. + const MCExpr *CPLoc = getTargetStreamer().addConstantPoolEntry(SubExprVal); + Operands.push_back(AArch64Operand::CreateImm(CPLoc, S, E, Ctx)); + return false; + } } } @@ -3811,7 +3856,8 @@ return parseDirectiveWord(8, Loc); if (IDVal == ".tlsdesccall") return parseDirectiveTLSDescCall(Loc); - + if (IDVal == ".ltorg" || IDVal == ".pool") + return parseDirectiveLtorg(Loc); return parseDirectiveLOH(IDVal, Loc); } @@ -3912,6 +3958,13 @@ return false; } +/// parseDirectiveLtorg +/// ::= .ltorg | .pool +bool AArch64AsmParser::parseDirectiveLtorg(SMLoc L) { + getTargetStreamer().emitCurrentConstantPool(); + return false; +} + bool AArch64AsmParser::classifySymbolRef(const MCExpr *Expr, AArch64MCExpr::VariantKind &ELFRefKind, Index: lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp =================================================================== --- /dev/null +++ lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp @@ -0,0 +1,40 @@ +//===- AArch64TargetStreamer.cpp - AArch64TargetStreamer class --*- C++ -*---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the AArch64TargetStreamer class. +// +//===----------------------------------------------------------------------===// +#include "llvm/ADT/MapVector.h" +#include "llvm/MC/ConstantPools.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCStreamer.h" + +using namespace llvm; + +// +// AArch64TargetStreamer Implemenation +// +AArch64TargetStreamer::AArch64TargetStreamer(MCStreamer &S) + : MCTargetStreamer(S), ConstantPools(new AssemblerConstantPools()) {} + +AArch64TargetStreamer::~AArch64TargetStreamer() {} + +// The constant pool handling is shared by all AArch64TargetStreamer +// implementations. +const MCExpr *AArch64TargetStreamer::addConstantPoolEntry(const MCExpr *Expr) { + return ConstantPools->addEntry(Streamer, Expr); +} + +void AArch64TargetStreamer::emitCurrentConstantPool() { + ConstantPools->emitForCurrentSection(Streamer); +} + +// finish() - write out any non-empty assembler constant pools. +void AArch64TargetStreamer::finish() { ConstantPools->emitAll(Streamer); } Index: lib/Target/AArch64/MCTargetDesc/CMakeLists.txt =================================================================== --- lib/Target/AArch64/MCTargetDesc/CMakeLists.txt +++ lib/Target/AArch64/MCTargetDesc/CMakeLists.txt @@ -7,6 +7,7 @@ AArch64MCExpr.cpp AArch64MCTargetDesc.cpp AArch64MachObjectWriter.cpp + AArch64TargetStreamer.cpp ) add_dependencies(LLVMAArch64Desc AArch64CommonTableGen) Index: test/CodeGen/AArch64/inlineasm-ldr-pseudo.ll =================================================================== --- /dev/null +++ test/CodeGen/AArch64/inlineasm-ldr-pseudo.ll @@ -0,0 +1,26 @@ +; We actually need to use -filetype=obj in this test because if we output +; assembly, the current code path will bypass the parser and just write the +; raw text out to the Streamer. We need to actually parse the inlineasm to +; demonstrate the bug. Going the asm->obj route does not show the issue. +; RUN: llc -mtriple=aarch64 < %s -filetype=obj | llvm-objdump -arch=aarch64 -d - | FileCheck %s + +; CHECK-LABEL: foo: +; CHECK: a0 79 95 d2 movz x0, #0xabcd +; CHECK: c0 03 5f d6 ret +define i32 @foo() nounwind { +entry: + %0 = tail call i32 asm sideeffect "ldr $0,=0xabcd", "=r"() nounwind + ret i32 %0 +} +; CHECK-LABEL: bar: +; CHECK: 40 00 00 58 ldr x0, #8 +; CHECK: c0 03 5f d6 ret +; Make sure the constant pool entry comes after the return +; CHECK-LABEL: $d.1: +define i32 @bar() nounwind { +entry: + %0 = tail call i32 asm sideeffect "ldr $0,=0x10001", "=r"() nounwind + ret i32 %0 +} + + Index: test/MC/AArch64/ldr-pseudo-obj-errors.s =================================================================== --- /dev/null +++ test/MC/AArch64/ldr-pseudo-obj-errors.s @@ -0,0 +1,13 @@ +//RUN: not llvm-mc -arch aarch64 -filetype=obj %s -o %t1 2> %t2 +//RUN: cat %t2 | FileCheck %s + +//These tests look for errors that should be reported for invalid object layout +//with the ldr pseudo. They are tested separately from parse errors because they +//only trigger when the file has successfully parsed and the object file is about +//to be written out. + +.text +foo: + ldr x0, =0x10111 + .space 0xdeadb0 +// CHECK: LVM ERROR: fixup value out of range Index: test/MC/AArch64/ldr-pseudo.s =================================================================== --- /dev/null +++ test/MC/AArch64/ldr-pseudo.s @@ -0,0 +1,231 @@ +//RUN: llvm-mc -arch aarch64 %s | FileCheck %s + +// +// Check that large constants are converted to ldr from constant pool +// +// simple test +.section a, "ax", @progbits +// CHECK-LABEL: f1: +f1: + ldr x0, =0x1234 +// CHECK: movz x0, #0x1234 + ldr w1, =0x4567 +// CHECK: movz w1, #0x4567 + ldr x0, =0x12340000 +// CHECK: movz x0, #0x1234, lsl #16 + ldr w1, =0x45670000 +// CHECK: movz w1, #0x4567, lsl #16 + ldr x0, =0xabc00000000 +// CHECK: movz x0, #0xabc, lsl #32 + ldr x0, =0xbeef000000000000 +// CHECK: movz x0, #0xbeef, lsl #48 + +.section b,"ax",@progbits +// CHECK-LABEL: f3: +f3: + ldr x0, =0x10001 +// CHECK: ldr x0, .Ltmp[[TMP0:[0-9]+]] + +// loading multiple constants +.section c,"ax",@progbits +// CHECK-LABEL: f4: +f4: + ldr x0, =0x10002 +// CHECK: ldr x0, .Ltmp[[TMP1:[0-9]+]] + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + ldr x0, =0x10003 +// CHECK: ldr x0, .Ltmp[[TMP2:[0-9]+]] + adds x0, x0, #1 + adds x0, x0, #1 + +// TODO: the same constants should have the same constant pool location +.section d,"ax",@progbits +// CHECK-LABEL: f5: +f5: + ldr x0, =0x10004 +// CHECK: ldr x0, .Ltmp[[TMP3:[0-9]+]] + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + ldr x0, =0x10004 +// CHECK: ldr x0, .Ltmp[[TMP4:[0-9]+]] + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + +// a section defined in multiple pieces should be merged and use a single constant pool +.section e,"ax",@progbits +// CHECK-LABEL: f6: +f6: + ldr x0, =0x10006 +// CHECK: ldr x0, .Ltmp[[TMP5:[0-9]+]] + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + +.section f, "ax", @progbits +// CHECK-LABEL: f7: +f7: + adds x0, x0, #1 + adds x0, x0, #1 + adds x0, x0, #1 + +.section e, "ax", @progbits +// CHECK-LABEL: f8: +f8: + adds x0, x0, #1 + ldr x0, =0x10007 +// CHECK: ldr x0, .Ltmp[[TMP6:[0-9]+]] + adds x0, x0, #1 + adds x0, x0, #1 + +// +// Check that symbols can be loaded using ldr pseudo +// + +// load an undefined symbol +.section g,"ax",@progbits +// CHECK-LABEL: f9: +f9: + ldr x0, =foo +// CHECK: ldr x0, .Ltmp[[TMP7:[0-9]+]] + +// load a symbol from another section +.section h,"ax",@progbits +// CHECK-LABEL: f10: +f10: + ldr x0, =f5 +// CHECK: ldr x0, .Ltmp[[TMP8:[0-9]+]] + +// load a symbol from the same section +.section i,"ax",@progbits +// CHECK-LABEL: f11: +f11: + ldr x0, =f12 +// CHECK: ldr x0, .Ltmp[[TMP9:[0-9]+]] + ldr w0,=0x3C000 +// CHECK: ldr w0, .Ltmp[[TMP10:[0-9]+]] + +// CHECK-LABEL: f12: +f12: + adds x0, x0, #1 + adds x0, x0, #1 + +.section j,"ax",@progbits +// mix of symbols and constants +// CHECK-LABEL: f13: +f13: + adds x0, x0, #1 + adds x0, x0, #1 + ldr x0, =0x101 +// CHECK: movz x0, #0x101 + adds x0, x0, #1 + adds x0, x0, #1 + ldr x0, =bar +// CHECK: ldr x0, .Ltmp[[TMP11:[0-9]+]] + adds x0, x0, #1 + adds x0, x0, #1 +// +// Check for correct usage in other contexts +// + +// usage in macro +.macro useit_in_a_macro + ldr x0, =0x10008 + ldr x0, =baz +.endm +.section k,"ax",@progbits +// CHECK-LABEL: f14: +f14: + useit_in_a_macro +// CHECK: ldr x0, .Ltmp[[TMP12:[0-9]+]] +// CHECK: ldr x0, .Ltmp[[TMP13:[0-9]+]] + +// usage with expressions +.section l, "ax", @progbits +// CHECK-LABEL: f15: +f15: + ldr x0, =0x10001+8 +// CHECK: ldr x0, .Ltmp[[TMP14:[0-9]+]] + adds x0, x0, #1 + ldr x0, =bar+4 +// CHECK: ldr x0, .Ltmp[[TMP15:[0-9]+]] + adds x0, x0, #1 + +// +// Constant Pools +// +// CHECK: .section b,"ax",@progbits +// CHECK: .align 2 +// CHECK: .Ltmp[[TMP0]] +// CHECK: .word 65537 + +// CHECK: .section c,"ax",@progbits +// CHECK: .align 2 +// CHECK: .Ltmp[[TMP1]] +// CHECK: .word 65538 +// CHECK: .Ltmp[[TMP2]] +// CHECK: .word 65539 + +// CHECK: .section d,"ax",@progbits +// CHECK: .align 2 +// CHECK: .Ltmp[[TMP3]] +// CHECK: .word 65540 +// CHECK: .Ltmp[[TMP4]] +// CHECK: .word 65540 + +// CHECK: .section e,"ax",@progbits +// CHECK: .align 2 +// CHECK: .Ltmp[[TMP5]] +// CHECK: .word 65542 +// CHECK: .Ltmp[[TMP6]] +// CHECK: .word 65543 + +// Should not switch to section because it has no constant pool +// CHECK-NOT: .section f,"ax",@progbits + +// CHECK: .section g,"ax",@progbits +// CHECK: .align 2 +// CHECK: .Ltmp[[TMP7]] +// CHECK: .word foo + +// CHECK: .section h,"ax",@progbits +// CHECK: .align 2 +// CHECK: .Ltmp[[TMP8]] +// CHECK: .word f5 + +// CHECK: .section i,"ax",@progbits +// CHECK: .align 2 +// CHECK: .Ltmp[[TMP9]] +// CHECK: .word f12 +// CHECK: .Ltmp[[TMP10]] +// CHECK: .word 245760 + +// CHECK: .section j,"ax",@progbits +// CHECK: .align 2 +// CHECK: .Ltmp[[TMP11]] +// CHECK: .word bar + +// CHECK: .section k,"ax",@progbits +// CHECK: .align 2 +// CHECK: .Ltmp[[TMP12]] +// CHECK: .word 65544 +// CHECK: .Ltmp[[TMP13]] +// CHECK: .word baz + +// CHECK: .section l,"ax",@progbits +// CHECK: .align 2 +// CHECK: .Ltmp[[TMP14]] +// CHECK: .word 65545 +// CHECK: .Ltmp[[TMP15]] +// CHECK: .word bar+4