Skip to content

Commit

Permalink
[LoongArch] Support .option directive
Browse files Browse the repository at this point in the history
The .option can accept 4 parameters like the LoongArch's gnu as:
push, pop, relax and norelax.

Reviewed By: heiher, SixWeining

Pull Request: llvm#110404
  • Loading branch information
wangleiat authored and DanielCChen committed Oct 16, 2024
1 parent 07c6fd7 commit fec88c0
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 0 deletions.
108 changes: 108 additions & 0 deletions llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "MCTargetDesc/LoongArchMCExpr.h"
#include "MCTargetDesc/LoongArchMCTargetDesc.h"
#include "MCTargetDesc/LoongArchMatInt.h"
#include "MCTargetDesc/LoongArchTargetStreamer.h"
#include "TargetInfo/LoongArchTargetInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstBuilder.h"
Expand All @@ -30,8 +31,16 @@ using namespace llvm;

namespace {
class LoongArchAsmParser : public MCTargetAsmParser {
SmallVector<FeatureBitset, 4> FeatureBitStack;

SMLoc getLoc() const { return getParser().getTok().getLoc(); }
bool is64Bit() const { return getSTI().hasFeature(LoongArch::Feature64Bit); }
LoongArchTargetStreamer &getTargetStreamer() {
assert(getParser().getStreamer().getTargetStreamer() &&
"do not have a target streamer");
MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer();
return static_cast<LoongArchTargetStreamer &>(TS);
}

struct Inst {
unsigned Opc;
Expand Down Expand Up @@ -60,6 +69,8 @@ class LoongArchAsmParser : public MCTargetAsmParser {
unsigned validateTargetOperandClass(MCParsedAsmOperand &Op,
unsigned Kind) override;

ParseStatus parseDirective(AsmToken DirectiveID) override;

bool generateImmOutOfRangeError(OperandVector &Operands, uint64_t ErrorInfo,
int64_t Lower, int64_t Upper,
const Twine &Msg);
Expand All @@ -81,6 +92,39 @@ class LoongArchAsmParser : public MCTargetAsmParser {

bool parseOperand(OperandVector &Operands, StringRef Mnemonic);

bool parseDirectiveOption();

void setFeatureBits(uint64_t Feature, StringRef FeatureString) {
if (!(getSTI().hasFeature(Feature))) {
MCSubtargetInfo &STI = copySTI();
setAvailableFeatures(
ComputeAvailableFeatures(STI.ToggleFeature(FeatureString)));
}
}

void clearFeatureBits(uint64_t Feature, StringRef FeatureString) {
if (getSTI().hasFeature(Feature)) {
MCSubtargetInfo &STI = copySTI();
setAvailableFeatures(
ComputeAvailableFeatures(STI.ToggleFeature(FeatureString)));
}
}

void pushFeatureBits() {
FeatureBitStack.push_back(getSTI().getFeatureBits());
}

bool popFeatureBits() {
if (FeatureBitStack.empty())
return true;

FeatureBitset FeatureBits = FeatureBitStack.pop_back_val();
copySTI().setFeatureBits(FeatureBits);
setAvailableFeatures(ComputeAvailableFeatures(FeatureBits));

return false;
}

// Helper to emit the sequence of instructions generated by the
// "emitLoadAddress*" functions.
void emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg,
Expand Down Expand Up @@ -1748,6 +1792,70 @@ bool LoongArchAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
llvm_unreachable("Unknown match type detected!");
}

bool LoongArchAsmParser::parseDirectiveOption() {
MCAsmParser &Parser = getParser();
// Get the option token.
AsmToken Tok = Parser.getTok();

// At the moment only identifiers are supported.
if (parseToken(AsmToken::Identifier, "expected identifier"))
return true;

StringRef Option = Tok.getIdentifier();

if (Option == "push") {
if (Parser.parseEOL())
return true;

getTargetStreamer().emitDirectiveOptionPush();
pushFeatureBits();
return false;
}

if (Option == "pop") {
SMLoc StartLoc = Parser.getTok().getLoc();
if (Parser.parseEOL())
return true;

getTargetStreamer().emitDirectiveOptionPop();
if (popFeatureBits())
return Error(StartLoc, ".option pop with no .option push");

return false;
}

if (Option == "relax") {
if (Parser.parseEOL())
return true;

getTargetStreamer().emitDirectiveOptionRelax();
setFeatureBits(LoongArch::FeatureRelax, "relax");
return false;
}

if (Option == "norelax") {
if (Parser.parseEOL())
return true;

getTargetStreamer().emitDirectiveOptionNoRelax();
clearFeatureBits(LoongArch::FeatureRelax, "relax");
return false;
}

// Unknown option.
Warning(Parser.getTok().getLoc(),
"unknown option, expected 'push', 'pop', 'relax' or 'norelax'");
Parser.eatToEndOfStatement();
return false;
}

ParseStatus LoongArchAsmParser::parseDirective(AsmToken DirectiveID) {
if (DirectiveID.getString() == ".option")
return parseDirectiveOption();

return ParseStatus::NoMatch;
}

extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchAsmParser() {
RegisterMCAsmParser<LoongArchAsmParser> X(getTheLoongArch32Target());
RegisterMCAsmParser<LoongArchAsmParser> Y(getTheLoongArch64Target());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ MCELFStreamer &LoongArchTargetELFStreamer::getStreamer() {
return static_cast<MCELFStreamer &>(Streamer);
}

void LoongArchTargetELFStreamer::emitDirectiveOptionPush() {}
void LoongArchTargetELFStreamer::emitDirectiveOptionPop() {}
void LoongArchTargetELFStreamer::emitDirectiveOptionRelax() {}
void LoongArchTargetELFStreamer::emitDirectiveOptionNoRelax() {}

void LoongArchTargetELFStreamer::finish() {
LoongArchTargetStreamer::finish();
ELFObjectWriter &W = getStreamer().getWriter();
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ class LoongArchTargetELFStreamer : public LoongArchTargetStreamer {
MCELFStreamer &getStreamer();
LoongArchTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI);

void emitDirectiveOptionPush() override;
void emitDirectiveOptionPop() override;
void emitDirectiveOptionRelax() override;
void emitDirectiveOptionNoRelax() override;

void finish() override;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ createLoongArchObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {
: nullptr;
}

static MCTargetStreamer *
createLoongArchAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS,
MCInstPrinter *InstPrint) {
return new LoongArchTargetAsmStreamer(S, OS);
}

namespace {

class LoongArchMCInstrAnalysis : public MCInstrAnalysis {
Expand Down Expand Up @@ -212,5 +218,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchTargetMC() {
TargetRegistry::RegisterELFStreamer(*T, createLoongArchELFStreamer);
TargetRegistry::RegisterObjectTargetStreamer(
*T, createLoongArchObjectTargetStreamer);
TargetRegistry::RegisterAsmTargetStreamer(*T,
createLoongArchAsmTargetStreamer);
}
}
26 changes: 26 additions & 0 deletions llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,29 @@ void LoongArchTargetStreamer::setTargetABI(LoongArchABI::ABI ABI) {
"Improperly initialized target ABI");
TargetABI = ABI;
}

void LoongArchTargetStreamer::emitDirectiveOptionPush() {}
void LoongArchTargetStreamer::emitDirectiveOptionPop() {}
void LoongArchTargetStreamer::emitDirectiveOptionRelax() {}
void LoongArchTargetStreamer::emitDirectiveOptionNoRelax() {}

// This part is for ascii assembly output.
LoongArchTargetAsmStreamer::LoongArchTargetAsmStreamer(
MCStreamer &S, formatted_raw_ostream &OS)
: LoongArchTargetStreamer(S), OS(OS) {}

void LoongArchTargetAsmStreamer::emitDirectiveOptionPush() {
OS << "\t.option\tpush\n";
}

void LoongArchTargetAsmStreamer::emitDirectiveOptionPop() {
OS << "\t.option\tpop\n";
}

void LoongArchTargetAsmStreamer::emitDirectiveOptionRelax() {
OS << "\t.option\trelax\n";
}

void LoongArchTargetAsmStreamer::emitDirectiveOptionNoRelax() {
OS << "\t.option\tnorelax\n";
}
19 changes: 19 additions & 0 deletions llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchTargetStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "LoongArch.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Support/FormattedStream.h"

namespace llvm {
class LoongArchTargetStreamer : public MCTargetStreamer {
Expand All @@ -21,6 +22,24 @@ class LoongArchTargetStreamer : public MCTargetStreamer {
LoongArchTargetStreamer(MCStreamer &S);
void setTargetABI(LoongArchABI::ABI ABI);
LoongArchABI::ABI getTargetABI() const { return TargetABI; }

virtual void emitDirectiveOptionPush();
virtual void emitDirectiveOptionPop();
virtual void emitDirectiveOptionRelax();
virtual void emitDirectiveOptionNoRelax();
};

// This part is for ascii assembly output.
class LoongArchTargetAsmStreamer : public LoongArchTargetStreamer {
formatted_raw_ostream &OS;

public:
LoongArchTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS);

void emitDirectiveOptionPush() override;
void emitDirectiveOptionPop() override;
void emitDirectiveOptionRelax() override;
void emitDirectiveOptionNoRelax() override;
};

} // end namespace llvm
Expand Down
23 changes: 23 additions & 0 deletions llvm/test/MC/LoongArch/Directives/option-invalid.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# RUN: not llvm-mc --triple=loongarch64 %s 2>&1 \
# RUN: | FileCheck --implicit-check-not=error: %s

# CHECK: :[[#@LINE+1]]:8: error: expected identifier
.option

# CHECK: :[[#@LINE+1]]:9: error: expected identifier
.option 123

# CHECK: :[[#@LINE+1]]:9: error: expected identifier
.option "str"

# CHECK: :[[#@LINE+1]]:12: warning: unknown option, expected 'push', 'pop', 'relax' or 'norelax'
.option bar

# CHECK: :[[#@LINE+1]]:12: error: .option pop with no .option push
.option pop

# CHECK: :[[#@LINE+1]]:14: error: expected newline
.option push 123

# CHECK: :[[#@LINE+1]]:13: error: expected newline
.option pop 123
59 changes: 59 additions & 0 deletions llvm/test/MC/LoongArch/Directives/option-pushpop.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# RUN: llvm-mc --triple=loongarch64 --mattr=-relax %s \
# RUN: | FileCheck --check-prefix=CHECK-ASM %s
# RUN: llvm-mc --triple=loongarch64 --mattr=-relax --filetype=obj %s \
# RUN: | llvm-readobj -r - | FileCheck --check-prefix=CHECK-RELOC %s

## Test the operation of the push and pop assembler directives when
## using .option relax. Checks that using .option pop correctly restores
## all target features to their state at the point where .option pop was
## last used.

# CHECK-ASM: .option push
.option push # relax = false

# CHECK-ASM: .option relax
.option relax # relax = true

# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym1)
# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym1)
# CHECK-RELOC: R_LARCH_PCALA_HI20 sym1 0x0
# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0
# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym1 0x0
# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0
la.pcrel $a0, sym1

# CHECK-ASM: .option push
.option push # relax = true

# CHECK-ASM: .option norelax
.option norelax # relax = false

# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym2)
# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym2)
# CHECK-RELOC-NEXT: R_LARCH_PCALA_HI20 sym2 0x0
# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0
# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym2 0x0
# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0
la.pcrel $a0, sym2

# CHECK-ASM: .option pop
.option pop # relax = true

# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym3)
# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym3)
# CHECK-RELOC: R_LARCH_PCALA_HI20 sym3 0x0
# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0
# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym3 0x0
# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0
la.pcrel $a0, sym3

# CHECK-ASM: .option pop
.option pop # relax = false

la.pcrel $a0, sym4
# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym4)
# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym4)
# CHECK-RELOC-NEXT: R_LARCH_PCALA_HI20 sym4 0x0
# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0
# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym4 0x0
# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0
30 changes: 30 additions & 0 deletions llvm/test/MC/LoongArch/Directives/option-relax.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# RUN: llvm-mc --triple=loongarch64 %s | FileCheck --check-prefix=CHECK-ASM %s
# RUN: llvm-mc -filetype=obj --triple=loongarch64 %s \
# RUN: | llvm-readobj -r - | FileCheck -check-prefix=CHECK-RELOC %s

## Check .option relax causes R_LARCH_RELAX to be emitted, and .option
## norelax suppresses it.

# CHECK-ASM: .option relax
.option relax

# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym1)
# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym1)

# CHECK-RELOC: R_LARCH_PCALA_HI20 sym1 0x0
# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0
# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym1 0x0
# CHECK-RELOC-NEXT: R_LARCH_RELAX - 0x0
la.pcrel $a0, sym1

# CHECK-ASM: .option norelax
.option norelax

# CHECK-ASM: pcalau12i $a0, %pc_hi20(sym2)
# CHECK-ASM-NEXT: addi.d $a0, $a0, %pc_lo12(sym2)

# CHECK-RELOC-NEXT: R_LARCH_PCALA_HI20 sym2 0x0
# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0
# CHECK-RELOC-NEXT: R_LARCH_PCALA_LO12 sym2 0x0
# CHECK-RELOC-NOT: R_LARCH_RELAX - 0x0
la.pcrel $a0, sym2

0 comments on commit fec88c0

Please sign in to comment.