Skip to content
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
110 changes: 104 additions & 6 deletions TargetBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <optional>
#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 <typename T> using LLVMGoOptional = Optional<T>;
#else
template <typename T> using LLVMGoOptional = std::optional<T>;
#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<Reloc::Model> 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::Model> 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;

Expand Down
7 changes: 3 additions & 4 deletions TargetBindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 9 additions & 4 deletions target.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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) {
Expand All @@ -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,
Expand All @@ -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),
Expand Down
125 changes: 124 additions & 1 deletion target_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@

package llvm

import "testing"
import (
"bytes"
"debug/elf"
"encoding/binary"
"strings"
"testing"
)

func TestCreateTargetMachineWithOptionsSectionOptions(t *testing.T) {
InitializeNativeTarget()
Expand Down Expand Up @@ -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)
}
Comment thread
cpunion marked this conversation as resolved.

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
}
}
Loading