Skip to content

Commit

Permalink
ARM IAS: support .inst directive
Browse files Browse the repository at this point in the history
This adds support for the .inst directive.  This is an ARM specific directive to
indicate an instruction encoded as a constant expression.  The major difference
between .word, .short, or .byte and .inst is that the latter will be
disassembled as an instruction since it does not get flagged as data.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@197657 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
compnerd committed Dec 19, 2013
1 parent 2ad4cf5 commit 304512c
Show file tree
Hide file tree
Showing 11 changed files with 317 additions and 0 deletions.
1 change: 1 addition & 0 deletions include/llvm/MC/MCStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class ARMTargetStreamer : public MCTargetStreamer {
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
Expand Down
80 changes: 80 additions & 0 deletions lib/Target/ARM/AsmParser/ARMAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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<MCConstantExpr>(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<ARMAsmParser> X(TheARMTarget);
Expand Down
52 changes: 52 additions & 0 deletions lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -295,6 +305,7 @@ class ARMTargetELFStreamer : 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();

size_t calculateContentSize() const;
Expand Down Expand Up @@ -367,6 +378,44 @@ class ARMELFStreamer : public MCELFStreamer {
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.
Expand Down Expand Up @@ -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();
Expand Down
15 changes: 15 additions & 0 deletions test/MC/ARM/inst-arm-suffixes.s
Original file line number Diff line number Diff line change
@@ -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

15 changes: 15 additions & 0 deletions test/MC/ARM/inst-constant-required.s
Original file line number Diff line number Diff line change
@@ -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

20 changes: 20 additions & 0 deletions test/MC/ARM/inst-directive-emit.s
Original file line number Diff line number Diff line change
@@ -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

81 changes: 81 additions & 0 deletions test/MC/ARM/inst-directive.s
Original file line number Diff line number Diff line change
@@ -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: )

14 changes: 14 additions & 0 deletions test/MC/ARM/inst-overflow.s
Original file line number Diff line number Diff line change
@@ -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


13 changes: 13 additions & 0 deletions test/MC/ARM/inst-thumb-overflow-2.s
Original file line number Diff line number Diff line change
@@ -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

13 changes: 13 additions & 0 deletions test/MC/ARM/inst-thumb-overflow.s
Original file line number Diff line number Diff line change
@@ -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

Loading

0 comments on commit 304512c

Please sign in to comment.