Skip to content

[LoongArch] Support .option directive #110404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading