diff --git a/TargetBindings.cpp b/TargetBindings.cpp index 81c42c2..8df99e5 100644 --- a/TargetBindings.cpp +++ b/TargetBindings.cpp @@ -8,22 +8,120 @@ #include "TargetBindings.h" +#include "llvm/Config/llvm-config.h" #include "llvm/Support/CBindingWrapping.h" #include "llvm/Target/TargetMachine.h" +#if LLVM_VERSION_MAJOR < 18 +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Target/CodeGenCWrappers.h" +#include "llvm/Target/TargetOptions.h" + +#if LLVM_VERSION_MAJOR < 16 +#include "llvm/ADT/Optional.h" +#else +#include +#endif +#endif + using namespace llvm; DEFINE_SIMPLE_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef) +#if LLVM_VERSION_MAJOR < 18 +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(Target, LLVMTargetRef) + +#if LLVM_VERSION_MAJOR < 16 +template using LLVMGoOptional = Optional; +#else +template using LLVMGoOptional = std::optional; +#endif + +static LLVMTargetMachineRef LLVMGoCreateTargetMachineWithLegacyOptions( + LLVMTargetRef T, const char *Triple, const char *CPU, const char *Features, + LLVMCodeGenOptLevel Level, LLVMRelocMode RelocMode, LLVMCodeModel CM, + const char *ABIName, LLVMBool FunctionSections, LLVMBool DataSections, + LLVMBool UniqueSectionNames) { + LLVMGoOptional RM; + switch (RelocMode) { + case LLVMRelocStatic: + RM = Reloc::Static; + break; + case LLVMRelocPIC: + RM = Reloc::PIC_; + break; + case LLVMRelocDynamicNoPic: + RM = Reloc::DynamicNoPIC; + break; + case LLVMRelocROPI: + RM = Reloc::ROPI; + break; + case LLVMRelocRWPI: + RM = Reloc::RWPI; + break; + case LLVMRelocROPI_RWPI: + RM = Reloc::ROPI_RWPI; + break; + default: + break; + } + + bool JIT = false; + LLVMGoOptional CodeModel = unwrap(CM, JIT); + + CodeGenOpt::Level OptLevel; + switch (Level) { + case LLVMCodeGenLevelNone: + OptLevel = CodeGenOpt::None; + break; + case LLVMCodeGenLevelLess: + OptLevel = CodeGenOpt::Less; + break; + case LLVMCodeGenLevelAggressive: + OptLevel = CodeGenOpt::Aggressive; + break; + default: + OptLevel = CodeGenOpt::Default; + break; + } + + TargetOptions Options; + Options.MCOptions.ABIName = ABIName; + Options.FunctionSections = !!FunctionSections; + Options.DataSections = !!DataSections; + Options.UniqueSectionNames = !!UniqueSectionNames; + return wrap(unwrap(T)->createTargetMachine(Triple, CPU, Features, Options, RM, + CodeModel, OptLevel, JIT)); +} +#endif + LLVMTargetMachineRef LLVMGoCreateTargetMachineWithOptions( - LLVMTargetRef T, const char *Triple, const char *CPU, - const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc, - LLVMCodeModel CodeModel, - LLVMBool FunctionSections, LLVMBool DataSections, + LLVMTargetRef T, const char *Triple, const char *CPU, const char *Features, + LLVMCodeGenOptLevel Level, LLVMRelocMode RelocMode, LLVMCodeModel CM, + const char *ABIName, LLVMBool FunctionSections, LLVMBool DataSections, LLVMBool UniqueSectionNames) { + if (!T) + return nullptr; + + if (!ABIName) + ABIName = ""; + +#if LLVM_VERSION_MAJOR >= 18 + LLVMTargetMachineOptionsRef Options = LLVMCreateTargetMachineOptions(); + LLVMTargetMachineOptionsSetCPU(Options, CPU); + LLVMTargetMachineOptionsSetFeatures(Options, Features); + LLVMTargetMachineOptionsSetABI(Options, ABIName); + LLVMTargetMachineOptionsSetCodeGenOptLevel(Options, Level); + LLVMTargetMachineOptionsSetRelocMode(Options, RelocMode); + LLVMTargetMachineOptionsSetCodeModel(Options, CM); LLVMTargetMachineRef TM = - LLVMCreateTargetMachine(T, Triple, CPU, Features, Level, Reloc, - CodeModel); + LLVMCreateTargetMachineWithOptions(T, Triple, Options); + LLVMDisposeTargetMachineOptions(Options); +#else + LLVMTargetMachineRef TM = LLVMGoCreateTargetMachineWithLegacyOptions( + T, Triple, CPU, Features, Level, RelocMode, CM, ABIName, FunctionSections, + DataSections, UniqueSectionNames); +#endif if (!TM) return nullptr; diff --git a/TargetBindings.h b/TargetBindings.h index d6d1fb5..cf9ad5a 100644 --- a/TargetBindings.h +++ b/TargetBindings.h @@ -17,10 +17,9 @@ extern "C" { #endif LLVMTargetMachineRef LLVMGoCreateTargetMachineWithOptions( - LLVMTargetRef T, const char *Triple, const char *CPU, - const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc, - LLVMCodeModel CodeModel, - LLVMBool FunctionSections, LLVMBool DataSections, + LLVMTargetRef T, const char *Triple, const char *CPU, const char *Features, + LLVMCodeGenOptLevel Level, LLVMRelocMode RelocMode, LLVMCodeModel CM, + const char *ABIName, LLVMBool FunctionSections, LLVMBool DataSections, LLVMBool UniqueSectionNames); LLVMBool LLVMGoTargetMachineFunctionSections(LLVMTargetMachineRef TM); LLVMBool LLVMGoTargetMachineDataSections(LLVMTargetMachineRef TM); diff --git a/target.go b/target.go index 6ef8753..b07c6bd 100644 --- a/target.go +++ b/target.go @@ -42,9 +42,12 @@ type ( CodeModel C.LLVMCodeModel ) -// TargetMachineOptions contains explicit llvm::TargetOptions flags exposed by +// TargetMachineOptions contains explicit target options exposed by // CreateTargetMachineWithOptions. type TargetMachineOptions struct { + // ABIName selects the target ABI (for example, "lp64d" on RISC-V). + // An empty string lets LLVM select its default ABI. + ABIName string // FunctionSections places each function in its own section. FunctionSections bool // DataSections places each data object in its own section. @@ -269,9 +272,8 @@ func (t Target) CreateTargetMachine(Triple string, CPU string, Features string, } // CreateTargetMachineWithOptions creates a new TargetMachine with explicit -// llvm::TargetOptions flags that are not exposed by the LLVM C API. -// Use CreateTargetMachine if you do not need to override TargetOptions fields -// that are absent from the LLVM C API. +// target options. ABIName is applied while LLVM constructs the target machine. +// Use CreateTargetMachine if you do not need to override any options. func (t Target) CreateTargetMachineWithOptions(Triple string, CPU string, Features string, Level CodeGenOptLevel, Reloc RelocMode, CodeModel CodeModel, opts TargetMachineOptions) (tm TargetMachine) { @@ -281,6 +283,8 @@ func (t Target) CreateTargetMachineWithOptions(Triple string, CPU string, Featur defer C.free(unsafe.Pointer(cCPU)) cFeatures := C.CString(Features) defer C.free(unsafe.Pointer(cFeatures)) + cABIName := C.CString(opts.ABIName) + defer C.free(unsafe.Pointer(cABIName)) tm.C = C.LLVMGoCreateTargetMachineWithOptions( t.C, @@ -290,6 +294,7 @@ func (t Target) CreateTargetMachineWithOptions(Triple string, CPU string, Featur C.LLVMCodeGenOptLevel(Level), C.LLVMRelocMode(Reloc), C.LLVMCodeModel(CodeModel), + cABIName, boolToLLVMBool(opts.FunctionSections), boolToLLVMBool(opts.DataSections), boolToLLVMBool(opts.UniqueSectionNames), diff --git a/target_test.go b/target_test.go index f03a73a..d487979 100644 --- a/target_test.go +++ b/target_test.go @@ -8,7 +8,13 @@ package llvm -import "testing" +import ( + "bytes" + "debug/elf" + "encoding/binary" + "strings" + "testing" +) func TestCreateTargetMachineWithOptionsSectionOptions(t *testing.T) { InitializeNativeTarget() @@ -53,3 +59,120 @@ func TestCreateTargetMachineWithOptionsSectionOptions(t *testing.T) { }) } } + +func TestCreateTargetMachineWithOptionsRISCVABIName(t *testing.T) { + InitializeAllTargetInfos() + InitializeAllTargets() + InitializeAllTargetMCs() + InitializeAllAsmPrinters() + + const ( + triple = "riscv64-unknown-elf" + features = "+m,+a,+f,+d,+c" + riscvFloatABIMask = uint32(0x6) + riscvFloatABIDouble = uint32(0x4) + ) + target, err := GetTargetFromTriple(triple) + if err != nil { + t.Skipf("RISC-V target not available: %v", err) + } + + tests := []struct { + abi string + wantELFFlags uint32 + wantFA0 bool + }{ + {abi: "lp64", wantELFFlags: 0, wantFA0: false}, + {abi: "lp64d", wantELFFlags: riscvFloatABIDouble, wantFA0: true}, + } + for _, tc := range tests { + t.Run(tc.abi, func(t *testing.T) { + tm := target.CreateTargetMachineWithOptions( + triple, "generic-rv64", features, + CodeGenLevelNone, RelocDefault, CodeModelDefault, + TargetMachineOptions{ABIName: tc.abi}, + ) + if tm.C == nil { + t.Fatal("CreateTargetMachineWithOptions returned a nil target machine") + } + defer tm.Dispose() + + ctx := NewContext() + defer ctx.Dispose() + mod := ctx.NewModule("riscv_abi_name_test") + defer mod.Dispose() + mod.SetTarget(triple) + td := tm.CreateTargetData() + defer td.Dispose() + mod.SetDataLayout(td.String()) + + calleeType := FunctionType(ctx.VoidType(), []Type{ctx.DoubleType()}, false) + callee := AddFunction(mod, "callee", calleeType) + callerType := FunctionType(ctx.VoidType(), nil, false) + caller := AddFunction(mod, "caller", callerType) + entry := AddBasicBlock(caller, "entry") + builder := ctx.NewBuilder() + defer builder.Dispose() + builder.SetInsertPointAtEnd(entry) + builder.CreateCall( + calleeType, callee, []Value{ConstFloat(ctx.DoubleType(), 1.25)}, "", + ) + builder.CreateRetVoid() + + if err := VerifyModule(mod, ReturnStatusAction); err != nil { + t.Fatal(err) + } + + object, err := tm.EmitToMemoryBuffer(mod, ObjectFile) + if err != nil { + t.Fatal(err) + } + defer object.Dispose() + flags := elfFlags(t, object.Bytes()) + if got := flags & riscvFloatABIMask; got != tc.wantELFFlags { + t.Fatalf("RISC-V ELF float ABI flags = %#x, want %#x (all flags %#x)", got, tc.wantELFFlags, flags) + } + + assembly, err := tm.EmitToMemoryBuffer(mod, AssemblyFile) + if err != nil { + t.Fatal(err) + } + defer assembly.Dispose() + asm := string(assembly.Bytes()) + if got := strings.Contains(asm, "fa0"); got != tc.wantFA0 { + t.Fatalf("assembly fa0 presence = %v, want %v:\n%s", got, tc.wantFA0, asm) + } + }) + } +} + +func elfFlags(t *testing.T, object []byte) uint32 { + t.Helper() + file, err := elf.NewFile(bytes.NewReader(object)) + if err != nil { + t.Fatal(err) + } + defer file.Close() + if file.Machine != elf.EM_RISCV { + t.Fatalf("ELF machine = %v, want %v", file.Machine, elf.EM_RISCV) + } + + reader := bytes.NewReader(object) + switch file.Class { + case elf.ELFCLASS32: + var header elf.Header32 + if err := binary.Read(reader, file.ByteOrder, &header); err != nil { + t.Fatal(err) + } + return header.Flags + case elf.ELFCLASS64: + var header elf.Header64 + if err := binary.Read(reader, file.ByteOrder, &header); err != nil { + t.Fatal(err) + } + return header.Flags + default: + t.Fatalf("unsupported ELF class %v", file.Class) + return 0 + } +}