diff --git a/include/llvm/MC/MCStreamer.h b/include/llvm/MC/MCStreamer.h index fce70dcad93..8ffce721ce7 100644 --- a/include/llvm/MC/MCStreamer.h +++ b/include/llvm/MC/MCStreamer.h @@ -95,6 +95,7 @@ public: virtual void emitFPU(unsigned FPU) = 0; virtual void emitArch(unsigned Arch) = 0; virtual void finishAttributeSection() = 0; + virtual void emitInst(uint32_t Inst, char Suffix = '\0') = 0; }; /// MCStreamer - Streaming machine code generation interface. This interface diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index bf73ba6ee65..6c9e445346b 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include "llvm/MC/MCAsmInfo.h" @@ -150,6 +151,7 @@ class ARMAsmParser : public MCTargetAsmParser { bool parseDirectiveSetFP(SMLoc L); bool parseDirectivePad(SMLoc L); bool parseDirectiveRegSave(SMLoc L, bool IsVector); + bool parseDirectiveInst(SMLoc L, char Suffix = '\0'); StringRef splitMnemonic(StringRef Mnemonic, unsigned &PredicationCode, bool &CarrySetting, unsigned &ProcessorIMod, @@ -7809,6 +7811,12 @@ bool ARMAsmParser::ParseDirective(AsmToken DirectiveID) { return parseDirectiveRegSave(DirectiveID.getLoc(), false); else if (IDVal == ".vsave") return parseDirectiveRegSave(DirectiveID.getLoc(), true); + else if (IDVal == ".inst") + return parseDirectiveInst(DirectiveID.getLoc()); + else if (IDVal == ".inst.n") + return parseDirectiveInst(DirectiveID.getLoc(), 'n'); + else if (IDVal == ".inst.w") + return parseDirectiveInst(DirectiveID.getLoc(), 'w'); return true; } @@ -8288,6 +8296,78 @@ bool ARMAsmParser::parseDirectiveRegSave(SMLoc L, bool IsVector) { return false; } +/// parseDirectiveInst +/// ::= .inst opcode [, ...] +/// ::= .inst.n opcode [, ...] +/// ::= .inst.w opcode [, ...] +bool ARMAsmParser::parseDirectiveInst(SMLoc Loc, char Suffix) { + int Width; + + if (isThumb()) { + switch (Suffix) { + case 'n': + Width = 2; + break; + case 'w': + Width = 4; + break; + default: + Parser.eatToEndOfStatement(); + return Error(Loc, "cannot determine Thumb instruction size, " + "use inst.n/inst.w instead"); + } + } else { + if (Suffix) { + Parser.eatToEndOfStatement(); + return Error(Loc, "width suffixes are invalid in ARM mode"); + } + Width = 4; + } + + if (getLexer().is(AsmToken::EndOfStatement)) { + Parser.eatToEndOfStatement(); + return Error(Loc, "expected expression following directive"); + } + + for (;;) { + const MCExpr *Expr; + + if (getParser().parseExpression(Expr)) + return Error(Loc, "expected expression"); + + const MCConstantExpr *Value = dyn_cast_or_null(Expr); + if (!Value) + return Error(Loc, "expected constant expression"); + + switch (Width) { + case 2: + if (Value->getValue() > 0xffff) + return Error(Loc, "inst.n operand is too big, use inst.w instead"); + break; + case 4: + if (Value->getValue() > 0xffffffff) + return Error(Loc, + StringRef(Suffix ? "inst.w" : "inst") + " operand is too big"); + break; + default: + llvm_unreachable("only supported widths are 2 and 4"); + } + + getTargetStreamer().emitInst(Value->getValue(), Suffix); + + if (getLexer().is(AsmToken::EndOfStatement)) + break; + + if (getLexer().isNot(AsmToken::Comma)) + return Error(Loc, "unexpected token in directive"); + + Parser.Lex(); + } + + Parser.Lex(); + return false; +} + /// Force static initialization. extern "C" void LLVMInitializeARMAsmParser() { RegisterMCAsmParser X(TheARMTarget); diff --git a/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp b/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp index deab9556407..2d2497895cc 100644 --- a/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp +++ b/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp @@ -20,8 +20,10 @@ #include "ARMUnwindOp.h" #include "ARMUnwindOpAsm.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" @@ -121,6 +123,7 @@ class ARMTargetAsmStreamer : public ARMTargetStreamer { virtual void emitTextAttribute(unsigned Attribute, StringRef String); virtual void emitArch(unsigned Arch); virtual void emitFPU(unsigned FPU); + virtual void emitInst(uint32_t Inst, char Suffix = '\0'); virtual void finishAttributeSection(); public: @@ -190,6 +193,13 @@ void ARMTargetAsmStreamer::emitFPU(unsigned FPU) { void ARMTargetAsmStreamer::finishAttributeSection() { } +void ARMTargetAsmStreamer::emitInst(uint32_t Inst, char Suffix) { + OS << "\t.inst"; + if (Suffix) + OS << "." << Suffix; + OS << "\t0x" << utohexstr(Inst) << "\n"; +} + class ARMTargetELFStreamer : public ARMTargetStreamer { private: // This structure holds all attributes, accounting for @@ -295,6 +305,7 @@ private: virtual void emitTextAttribute(unsigned Attribute, StringRef String); virtual void emitArch(unsigned Arch); virtual void emitFPU(unsigned FPU); + virtual void emitInst(uint32_t Inst, char Suffix = '\0'); virtual void finishAttributeSection(); size_t calculateContentSize() const; @@ -367,6 +378,44 @@ public: MCELFStreamer::EmitInstruction(Inst); } + virtual void emitInst(uint32_t Inst, char Suffix) { + unsigned Size; + char Buffer[4]; + const bool LittleEndian = getContext().getAsmInfo()->isLittleEndian(); + + switch (Suffix) { + case '\0': + Size = 4; + + assert(!IsThumb); + EmitARMMappingSymbol(); + for (unsigned II = 0, IE = Size; II != IE; II++) { + const unsigned I = LittleEndian ? (Size - II - 1) : II; + Buffer[Size - II - 1] = uint8_t(Inst >> I * CHAR_BIT); + } + + break; + case 'n': + case 'w': + Size = (Suffix == 'n' ? 2 : 4); + + assert(IsThumb); + EmitThumbMappingSymbol(); + for (unsigned II = 0, IE = Size; II != IE; II = II + 2) { + const unsigned I0 = LittleEndian ? II + 0 : (Size - II - 1); + const unsigned I1 = LittleEndian ? II + 1 : (Size - II - 2); + Buffer[Size - II - 2] = uint8_t(Inst >> I0 * CHAR_BIT); + Buffer[Size - II - 1] = uint8_t(Inst >> I1 * CHAR_BIT); + } + + break; + default: + llvm_unreachable("Invalid Suffix"); + } + + MCELFStreamer::EmitBytes(StringRef(Buffer, Size)); + } + /// This is one of the functions used to emit data into an ELF section, so the /// ARM streamer overrides it to add the appropriate mapping symbol ($d) if /// necessary. @@ -791,6 +840,9 @@ void ARMTargetELFStreamer::finishAttributeSection() { Contents.clear(); FPU = ARM::INVALID_FPU; } +void ARMTargetELFStreamer::emitInst(uint32_t Inst, char Suffix) { + getStreamer().emitInst(Inst, Suffix); +} void ARMELFStreamer::FinishImpl() { MCTargetStreamer &TS = getTargetStreamer(); diff --git a/test/MC/ARM/inst-arm-suffixes.s b/test/MC/ARM/inst-arm-suffixes.s new file mode 100644 index 00000000000..a80ef47a607 --- /dev/null +++ b/test/MC/ARM/inst-arm-suffixes.s @@ -0,0 +1,15 @@ +@ RUN: not llvm-mc %s -triple armv7-linux-gnueabi -filetype asm -o - 2>&1 \ +@ RUN: | FileCheck -check-prefix CHECK-ERROR %s + + .syntax unified + .arm + + .align 2 + .global suffixes_invalid_in_arm + .type suffixes_invalid_in_arm,%function +suffixes_invalid_in_arm: + .inst.n 2 +@ CHECK-ERROR: width suffixes are invalid in ARM mode + .inst.w 4 +@ CHECK-ERROR: width suffixes are invalid in ARM mode + diff --git a/test/MC/ARM/inst-constant-required.s b/test/MC/ARM/inst-constant-required.s new file mode 100644 index 00000000000..d4863dd2cd7 --- /dev/null +++ b/test/MC/ARM/inst-constant-required.s @@ -0,0 +1,15 @@ +@ RUN: not llvm-mc %s -triple=armv7-linux-gnueabi -filetype asm -o - 2>&1 \ +@ RUN: | FileCheck -check-prefix CHECK-ERROR %s + + .syntax unified + .arm + + .align 2 + .global constant_expression_required + .type constant_expression_required,%function +constant_expression_required: +.Label: + movs r0, r0 + .inst .Label +@ CHECK-ERROR: expected constant expression + diff --git a/test/MC/ARM/inst-directive-emit.s b/test/MC/ARM/inst-directive-emit.s new file mode 100644 index 00000000000..13b7edfa1f3 --- /dev/null +++ b/test/MC/ARM/inst-directive-emit.s @@ -0,0 +1,20 @@ +@ RUN: llvm-mc %s -triple armv7-linux-gnueabi -filetype asm -o - | FileCheck %s + + .syntax unified + .thumb + + .align 2 + .global emit_asm + .type emit_asm,%function +emit_asm: + .inst.w 0xf2400000, 0xf2c00000 + +@ CHECK: .text +@ CHECK: .code 16 +@ CHECK: .align 2 +@ CHECK: .globl emit_asm +@ CHECK: .type emit_asm,%function +@ CHECK: emit_asm: +@ CHECK: inst.w 0xF2400000 +@ CHECK: inst.w 0xF2C00000 + diff --git a/test/MC/ARM/inst-directive.s b/test/MC/ARM/inst-directive.s new file mode 100644 index 00000000000..a3fd8c23f44 --- /dev/null +++ b/test/MC/ARM/inst-directive.s @@ -0,0 +1,81 @@ +@ RUN: llvm-mc %s -triple=armv7-linux-gnueabi -filetype=obj -o - \ +@ RUN: | llvm-readobj -s -sd | FileCheck %s + + .syntax unified + +@------------------------------------------------------------------------------- +@ arm_inst +@------------------------------------------------------------------------------- + .arm + + .section .inst.arm_inst + + .align 2 + .global arm_inst + .type arm_inst,%function +arm_inst: + .inst 0xdefe + +@ CHECK: Section { +@ CHECK: Name: .inst.arm_inst +@ CHECK: SectionData ( +@ CHECK-NEXT: 0000: FEDE0000 +@ CHECK-NEXT: ) + +@------------------------------------------------------------------------------- +@ thumb_inst_n +@------------------------------------------------------------------------------- + .thumb + + .section .inst.thumb_inst_n + + .align 2 + .global thumb_inst_n + .type thumb_inst_n,%function +thumb_inst_n: + .inst.n 0xdefe + +@ CHECK: Section { +@ CHECK: Name: .inst.thumb_inst_n +@ CHECK: SectionData ( +@ CHECK-NEXT: 0000: FEDE +@ CHECK-NEXT: ) + +@------------------------------------------------------------------------------- +@ thumb_inst_w +@------------------------------------------------------------------------------- + .thumb + + .section .inst.thumb_inst_w + + .align 2 + .global thumb_inst_w + .type thumb_inst_w,%function +thumb_inst_w: + .inst.w 0x00000000 + +@ CHECK: Section { +@ CHECK: Name: .inst.thumb_inst_w +@ CHECK: SectionData ( +@ CHECK-NEXT: 0000: 00000000 +@ CHECK-NEXT: ) + +@------------------------------------------------------------------------------- +@ thumb_inst_w +@------------------------------------------------------------------------------- + .thumb + + .section .inst.thumb_inst_inst + + .align 2 + .global thumb_inst_inst + .type thumb_inst_inst,%function +thumb_inst_inst: + .inst.w 0xf2400000, 0xf2c00000 + +@ CHECK: Section { +@ CHECK: Name: .inst.thumb_inst_inst +@ CHECK: SectionData ( +@ CHECK-NEXT: 0000: 40F20000 C0F20000 +@ CHECK-NEXT: ) + diff --git a/test/MC/ARM/inst-overflow.s b/test/MC/ARM/inst-overflow.s new file mode 100644 index 00000000000..133d53f7430 --- /dev/null +++ b/test/MC/ARM/inst-overflow.s @@ -0,0 +1,14 @@ +@ RUN: not llvm-mc %s -triple armv7-linux-gnueabi -filetype asm -o - 2>&1 \ +@ RUN: | FileCheck -check-prefix CHECK-ERROR %s + + .syntax unified + .arm + + .align 2 + .global constant_overflow + .type constant_overflow,%function +constant_overflow: + .inst 1 << 32 +@ CHECK-ERROR: inst operand is too big + + diff --git a/test/MC/ARM/inst-thumb-overflow-2.s b/test/MC/ARM/inst-thumb-overflow-2.s new file mode 100644 index 00000000000..1b3d642cf66 --- /dev/null +++ b/test/MC/ARM/inst-thumb-overflow-2.s @@ -0,0 +1,13 @@ +@ RUN: not llvm-mc %s -triple armv7-linux-gnueabi -filetype asm -o - 2>&1 \ +@ RUN: | FileCheck -check-prefix CHECK-ERRORS %s + + .syntax unified + .thumb + + .align 2 + .global constant_overflow + .type constant_overflow,%function +constant_overflow: + .inst.w 1 << 32 +@ CHECK-ERRORS: inst.w operand is too big + diff --git a/test/MC/ARM/inst-thumb-overflow.s b/test/MC/ARM/inst-thumb-overflow.s new file mode 100644 index 00000000000..34626f97ff0 --- /dev/null +++ b/test/MC/ARM/inst-thumb-overflow.s @@ -0,0 +1,13 @@ +@ RUN: not llvm-mc %s -triple armv7-linux-gnueabi -filetype asm -o - 2>&1 \ +@ RUN: | FileCheck -check-prefix CHECK-ERROR %s + + .syntax unified + .thumb + + .align 2 + .global constant_overflow + .type constant_overflow,%function +constant_overflow: + .inst.n 1 << 31 +@ CHECK-ERROR: inst.n operand is too big, use inst.w instead + diff --git a/test/MC/ARM/inst-thumb-suffixes.s b/test/MC/ARM/inst-thumb-suffixes.s new file mode 100644 index 00000000000..40def3c3b89 --- /dev/null +++ b/test/MC/ARM/inst-thumb-suffixes.s @@ -0,0 +1,13 @@ +@ RUN: not llvm-mc %s -triple armv7-linux-gnueabi -filetype asm -o - 2>&1 \ +@ RUN: | FileCheck -check-prefix CHECK-ERROR %s + + .syntax unified + .thumb + + .align 2 + .global suffixes_required_in_thumb + .type suffixes_required_in_thumb,%function +suffixes_required_in_thumb: + .inst 0x0000 +@ CHECK-ERROR: cannot determine Thumb instruction size, use inst.n/inst.w instead +