diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -406,6 +406,12 @@ #include "ELFRelocs/AArch64.def" }; +// Special values for the st_other field in the symbol table entry for AArch64. +enum { + // Symbol may follow different calling convention than base PCS. + STO_AARCH64_VARIANT_PCS = 0x80 +}; + // ARM Specific e_flags enum : unsigned { EF_ARM_SOFT_FLOAT = 0x00000200U, // Legacy pre EABI_VER5 diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -89,6 +89,8 @@ void emitStartOfAsmFile(Module &M) override; void emitJumpTableInfo() override; + void emitFunctionEntryLabel() override; + void LowerJumpTableDest(MCStreamer &OutStreamer, const MachineInstr &MI); void LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM, @@ -787,6 +789,19 @@ } } +void AArch64AsmPrinter::emitFunctionEntryLabel() { + if (MF->getFunction().getCallingConv() == CallingConv::AArch64_VectorCall || + MF->getFunction().getCallingConv() == + CallingConv::AArch64_SVE_VectorCall || + STI->getRegisterInfo()->hasSVEArgsOrReturn(MF)) { + auto *TS = + static_cast(OutStreamer->getTargetStreamer()); + TS->emitDirectiveVariantPCS(CurrentFnSym); + } + + return AsmPrinter::emitFunctionEntryLabel(); +} + /// Small jump tables contain an unsigned byte or half, representing the offset /// from the lowest-addressed possible destination to the desired basic /// block. Since all instructions are 4-byte aligned, this is further compressed diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp --- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -184,6 +184,8 @@ bool parseDirectiveCFINegateRAState(); bool parseDirectiveCFIBKeyFrame(); + bool parseDirectiveVariantPCS(SMLoc L); + bool parseDirectiveSEHAllocStack(SMLoc L); bool parseDirectiveSEHPrologEnd(SMLoc L); bool parseDirectiveSEHSaveR19R20X(SMLoc L); @@ -5171,6 +5173,8 @@ parseDirectiveCFIBKeyFrame(); else if (IDVal == ".arch_extension") parseDirectiveArchExtension(Loc); + else if (IDVal == ".variant_pcs") + parseDirectiveVariantPCS(Loc); else if (IsMachO) { if (IDVal == MCLOHDirectiveName()) parseDirectiveLOH(IDVal, Loc); @@ -5650,6 +5654,32 @@ return false; } +/// parseDirectiveVariantPCS +/// ::= .variant_pcs symbolname +bool AArch64AsmParser::parseDirectiveVariantPCS(SMLoc L) { + MCAsmParser &Parser = getParser(); + + const AsmToken &Tok = Parser.getTok(); + if (Tok.isNot(AsmToken::Identifier)) + return TokError("expected symbol name"); + + StringRef SymbolName = Tok.getIdentifier(); + + MCSymbol *Sym = getContext().lookupSymbol(SymbolName); + if (!Sym) + return TokError("unknown symbol in '.variant_pcs' directive"); + + Parser.Lex(); // Eat the symbol + + // Shouldn't be any more tokens + if (parseToken(AsmToken::EndOfStatement)) + return addErrorSuffix(" in '.variant_pcs' directive"); + + getTargetStreamer().emitDirectiveVariantPCS(Sym); + + return false; +} + /// parseDirectiveSEHAllocStack /// ::= .seh_stackalloc bool AArch64AsmParser::parseDirectiveSEHAllocStack(SMLoc L) { diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp @@ -47,6 +47,10 @@ void emitInst(uint32_t Inst) override; + void emitDirectiveVariantPCS(MCSymbol *Symbol) override { + OS << "\t.variant_pcs " << Symbol->getName() << "\n"; + } + void EmitARM64WinCFIAllocStack(unsigned Size) override { OS << "\t.seh_stackalloc " << Size << "\n"; } @@ -249,6 +253,10 @@ getStreamer().emitInst(Inst); } +void AArch64TargetELFStreamer::emitDirectiveVariantPCS(MCSymbol *Symbol) { + cast(Symbol)->setOther(ELF::STO_AARCH64_VARIANT_PCS); +} + MCTargetStreamer *createAArch64AsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, MCInstPrinter *InstPrint, diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h @@ -39,6 +39,9 @@ /// Callback used to implement the .inst directive. virtual void emitInst(uint32_t Inst); + /// Callback used to implement the .variant_pcs directive. + virtual void emitDirectiveVariantPCS(MCSymbol *Symbol) {}; + virtual void EmitARM64WinCFIAllocStack(unsigned Size) {} virtual void EmitARM64WinCFISaveR19R20X(int Offset) {} virtual void EmitARM64WinCFISaveFPLR(int Offset) {} @@ -73,6 +76,7 @@ AArch64ELFStreamer &getStreamer(); void emitInst(uint32_t Inst) override; + void emitDirectiveVariantPCS(MCSymbol *Symbol) override; public: AArch64TargetELFStreamer(MCStreamer &S) : AArch64TargetStreamer(S) {} diff --git a/llvm/test/CodeGen/AArch64/variant-pcs.ll b/llvm/test/CodeGen/AArch64/variant-pcs.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/variant-pcs.ll @@ -0,0 +1,51 @@ +; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sve -o - %s | FileCheck %s --check-prefix=CHECK-ASM +; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sve -filetype=obj -o - %s \ +; RUN: | llvm-readobj --symbols - | FileCheck %s --check-prefix=CHECK-OBJ + +define i32 @base_pcs() { +; CHECK-ASM-LABEL: base_pcs: +; CHECK-ASM-NOT: .variant_pcs +; CHECK-OBJ-LABEL: Name: base_pcs +; CHECK-OBJ: Other: 0 + ret i32 42 +} + +define aarch64_vector_pcs <4 x i32> @neon_vector_pcs_1(<4 x i32> %arg) { +; CHECK-ASM: .variant_pcs neon_vector_pcs_1 +; CHECK-ASM-NEXT: neon_vector_pcs_1: +; CHECK-OBJ-LABEL: Name: neon_vector_pcs_1 +; CHECK-OBJ: Other [ (0x80) + ret <4 x i32> %arg +} + +define @sve_vector_pcs_1() { +; CHECK-ASM: .variant_pcs sve_vector_pcs_1 +; CHECK-ASM-NEXT: sve_vector_pcs_1: +; CHECK-OBJ-LABEL: Name: sve_vector_pcs_1 +; CHECK-OBJ: Other [ (0x80) + ret undef +} + +define @sve_vector_pcs_2() { +; CHECK-ASM: .variant_pcs sve_vector_pcs_2 +; CHECK-ASM-NEXT: sve_vector_pcs_2: +; CHECK-OBJ-LABEL: Name: sve_vector_pcs_2 +; CHECK-OBJ: Other [ (0x80) + ret undef +} + +define void @sve_vector_pcs_3( %arg) { +; CHECK-ASM: .variant_pcs sve_vector_pcs_3 +; CHECK-ASM-NEXT: sve_vector_pcs_3: +; CHECK-OBJ-LABEL: Name: sve_vector_pcs_3 +; CHECK-OBJ: Other [ (0x80) + ret void +} + +define void @sve_vector_pcs_4( %arg) { +; CHECK-ASM: .variant_pcs sve_vector_pcs_4 +; CHECK-ASM-NEXT: sve_vector_pcs_4: +; CHECK-OBJ-LABEL: Name: sve_vector_pcs_4 +; CHECK-OBJ: Other [ (0x80) + ret void +} diff --git a/llvm/test/MC/AArch64/directive-variant_pcs-err.s b/llvm/test/MC/AArch64/directive-variant_pcs-err.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/AArch64/directive-variant_pcs-err.s @@ -0,0 +1,17 @@ +// RUN: not llvm-mc -triple aarch64-unknown-none-eabi -filetype asm -o - %s 2>&1 | FileCheck %s + +.variant_pcs +// CHECK: error: expected symbol name +// CHECK-NEXT: .variant_pcs +// CHECK-NEXT: ^ + +.variant_pcs foo +// CHECK: error: unknown symbol in '.variant_pcs' directive +// CHECK-NEXT: .variant_pcs foo +// CHECK-NEXT: ^ + +.global foo +.variant_pcs foo bar +// CHECK: error: unexpected token in '.variant_pcs' directive +// CHECK-NEXT: .variant_pcs foo bar +// CHECK-NEXT: ^ diff --git a/llvm/test/MC/AArch64/directive-variant_pcs.s b/llvm/test/MC/AArch64/directive-variant_pcs.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/AArch64/directive-variant_pcs.s @@ -0,0 +1,11 @@ +// RUN: llvm-mc -triple aarch64-elf -filetype asm -o - %s | FileCheck %s +// RUN: llvm-mc -triple aarch64-elf -filetype obj -o - %s \ +// RUN: | llvm-readobj --symbols - | FileCheck %s --check-prefix=CHECK-ST_OTHER + +.text +.global foo +.variant_pcs foo +// CHECK: .variant_pcs foo + +// CHECK-ST_OTHER: Name: foo +// CHECK-ST_OTHER: Other [ (0x80)