From 6662759bc8cc47c6a0d4f5e2484701b9b2b4116b Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Mon, 17 Feb 2025 21:17:56 -0600 Subject: [PATCH] wip emit --- src/LLVM.zig | 100 +++++++- src/llvm.cpp | 643 +++++++++++++++++++++++++++++++++++++++++++++-- src/llvm_api.zig | 26 +- 3 files changed, 723 insertions(+), 46 deletions(-) diff --git a/src/LLVM.zig b/src/LLVM.zig index e6aec7f..778fb48 100644 --- a/src/LLVM.zig +++ b/src/LLVM.zig @@ -1,6 +1,68 @@ const lib = @import("lib.zig"); const Arena = lib.Arena; const api = @import("llvm_api.zig"); + +/// This is a String which ABI-compatible with C++ +pub const String = extern struct { + pointer: [*]const u8, + length: usize, + + pub fn from_slice(slice: []const u8) String { + return String{ + .pointer = slice.ptr, + .length = slice.len, + }; + } + + pub fn to_slice(string: String) []const u8 { + return string.pointer[0..string.length]; + } +}; + +pub const CodeModel = enum(u8) { + none = 0, + tiny = 1, + small = 2, + kernel = 3, + medium = 4, + large = 5, +}; + +pub const RelocationModel = enum(u8) { + default_relocation = 0, + static_relocation = 1, + pic = 2, + dynamic_no_pic = 3, + ropi = 4, + rwpi = 5, + ropi_rwpi = 6, +}; + +pub const CodeGenerationOptimizationLevel = enum(u8) { + none = 0, // -O0 + less = 1, // -O1 + normal = 2, // -O2, -Os + aggressive = 3, // -O3 +}; + +/// This is ABI-compatible with C++ +pub const TargetOptions = extern struct { + reserved: u32, +}; + +/// This is ABI-compatible with C++ +pub const TargetMachineCreate = extern struct { + target_options: TargetOptions, + cpu_triple: String, + cpu_model: String, + cpu_features: String, + code_model: CodeModel, + relocation_model: RelocationModel, + optimization_level: CodeGenerationOptimizationLevel, + jit: bool, + reserved: u32, +}; + pub const Architecture = enum { X86, }; @@ -18,11 +80,11 @@ const targets = [@typeInfo(Architecture).@"enum".fields.len]type{ pub const Context = opaque { pub const create = api.LLVMContextCreate; pub fn create_module(context: *Context, name: [:0]const u8) *Module { - return api.llvm_context_create_module(context, name.ptr, name.len); + return api.llvm_context_create_module(context, String.from_slice(name)); } pub const create_builder = api.LLVMCreateBuilderInContext; pub fn create_basic_block(context: *Context, name: []const u8, parent: *Function) *BasicBlock { - return api.llvm_context_create_basic_block(context, name.ptr, name.len, parent); + return api.llvm_context_create_basic_block(context, String.from_slice(name), parent); } }; @@ -32,9 +94,7 @@ pub const Module = opaque { pub const create_di_builder = api.LLVMCreateDIBuilder; pub fn to_string(module: *Module) []const u8 { - var result: []const u8 = undefined; - api.llvm_module_to_string(module, &result.ptr, &result.len); - return result; + return api.llvm_module_to_string(module).to_slice(); } const FunctionCreate = struct { @@ -45,12 +105,14 @@ pub const Module = opaque { }; pub fn create_function(module: *Module, create: FunctionCreate) *Function { - return api.llvm_module_create_function(module, create.type, create.linkage, create.address_space, create.name.ptr, create.name.len); + return api.llvm_module_create_function(module, create.type, create.linkage, create.address_space, String.from_slice(create.name)); } pub fn verify(module: *Module) VerifyResult { var result: VerifyResult = undefined; - result.success = api.llvm_module_verify(module, &result.error_message.ptr, &result.error_message.len); + var string: String = undefined; + result.success = api.llvm_module_verify(module, &string); + result.error_message = string.to_slice(); return result; } }; @@ -73,7 +135,9 @@ pub const Builder = opaque { pub const Function = opaque { pub fn verify(function: *Function) VerifyResult { var result: VerifyResult = undefined; - result.success = api.llvm_function_verify(function, &result.error_message.ptr, &result.error_message.len); + var string: String = undefined; + result.success = api.llvm_function_verify(function, &string); + result.error_message = string.to_slice(); return result; } }; @@ -236,18 +300,30 @@ pub const Thread = struct { } }; -pub var threads: []Thread = undefined; +const Global = struct { + threads: []Thread, + host_triple: []const u8, + host_cpu_model: []const u8, + host_cpu_features: []const u8, +}; +pub var global: Global = undefined; // This is meant to call globally, only once per execution pub fn initialize_all() void { - threads = lib.global.arena.allocate(Thread, lib.global.thread_count); inline for (targets) |target| { target.initialize(.{}); } + + global = .{ + .threads = lib.global.arena.allocate(Thread, lib.global.thread_count), + .host_triple = api.llvm_default_target_triple().to_slice(), + .host_cpu_model = api.llvm_host_cpu_name().to_slice(), + .host_cpu_features = api.llvm_host_cpu_features().to_slice(), + }; } pub fn experiment() void { - const thread = &threads[0]; + const thread = &global.threads[0]; thread.initialize(); const module = thread.context.create_module("first_module"); const builder = thread.context.create_builder(); @@ -273,8 +349,6 @@ pub fn experiment() void { unreachable; } - const module_z = api.LLVMPrintModuleToString(module); - _ = module_z; const module_string = module.to_string(); lib.print_string(module_string); } diff --git a/src/llvm.cpp b/src/llvm.cpp index 6e41734..5ec99a9 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -1,17 +1,37 @@ +#include #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" +#include "llvm/TargetParser/Host.h" +#include "llvm/TargetParser/SubtargetFeature.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/MC/TargetRegistry.h" #define EXPORT extern "C" #define fn static +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + using namespace llvm; -EXPORT Module* llvm_context_create_module(LLVMContext& context, const char* name_pointer, size_t name_length) +struct BBLLVMString { - auto name = StringRef(name_pointer, name_length); - return new Module(name, context); + const char* pointer; + size_t length; + + inline StringRef string_ref() const + { + return { pointer, length }; + } +}; + +EXPORT Module* llvm_context_create_module(LLVMContext& context, BBLLVMString name) +{ + return new Module(name.string_ref(), context); } EXPORT Value* llvm_builder_create_add(IRBuilder<>& builder, Value* left, Value* right, bool nuw, bool nsw) @@ -20,18 +40,16 @@ EXPORT Value* llvm_builder_create_add(IRBuilder<>& builder, Value* left, Value* return result; } -EXPORT Function* llvm_module_create_function(Module* module, FunctionType* function_type, GlobalValue::LinkageTypes linkage_type, unsigned address_space, const char* name_pointer, size_t name_length) +EXPORT Function* llvm_module_create_function(Module* module, FunctionType* function_type, GlobalValue::LinkageTypes linkage_type, unsigned address_space, BBLLVMString name) { - auto name = StringRef(name_pointer, name_length); - auto* function = Function::Create(function_type, linkage_type, address_space, name, module); + auto* function = Function::Create(function_type, linkage_type, address_space, name.string_ref(), module); return function; } -EXPORT StructType* llvm_context_create_struct_type(LLVMContext& context, Type** type_pointer, size_t type_count, const char* name_pointer, size_t name_length, bool is_packed) +EXPORT StructType* llvm_context_create_struct_type(LLVMContext& context, Type** type_pointer, size_t type_count, BBLLVMString name, bool is_packed) { auto types = ArrayRef(type_pointer, type_count); - auto name = StringRef(name_pointer, name_length); - auto* struct_type = StructType::create(context, types, name, is_packed); + auto* struct_type = StructType::create(context, types, name.string_ref(), is_packed); return struct_type; } @@ -42,14 +60,13 @@ EXPORT StructType* llvm_context_get_struct_type(LLVMContext& context, Type** typ return struct_type; } -EXPORT BasicBlock* llvm_context_create_basic_block(LLVMContext& context, const char* name_pointer, size_t name_length, Function* parent) +EXPORT BasicBlock* llvm_context_create_basic_block(LLVMContext& context, BBLLVMString name, Function* parent) { - auto name = StringRef(name_pointer, name_length); - auto* basic_block = BasicBlock::Create(context, name, parent); + auto* basic_block = BasicBlock::Create(context, name.string_ref(), parent); return basic_block; } -fn void stream_to_string(raw_string_ostream& stream, const char** message_pointer, size_t* message_length) +fn BBLLVMString stream_to_string(raw_string_ostream& stream) { // No need to call stream.flush(); because it's string-based stream.flush(); @@ -64,41 +81,623 @@ fn void stream_to_string(raw_string_ostream& stream, const char** message_pointe memcpy(result, string.c_str(), length); } - *message_pointer = result; - *message_length = length; + return { result, length }; } -EXPORT bool llvm_function_verify(Function& function, const char** message_pointer, size_t* message_length) +EXPORT bool llvm_function_verify(Function& function, BBLLVMString* error_message) { std::string message_buffer; raw_string_ostream message_stream(message_buffer); bool result = verifyFunction(function, &message_stream); auto size = message_stream.str().size(); - stream_to_string(message_stream, message_pointer, message_length); + *error_message = stream_to_string(message_stream); // We invert the condition because LLVM conventions are just stupid return !result; } -EXPORT bool llvm_module_verify(const Module& module, const char** message_pointer, size_t* message_length) +EXPORT bool llvm_module_verify(const Module& module, BBLLVMString* error_message) { std::string message_buffer; raw_string_ostream message_stream(message_buffer); bool result = verifyModule(module, &message_stream); - stream_to_string(message_stream, message_pointer, message_length); + *error_message = stream_to_string(message_stream); // We invert the condition because LLVM conventions are just stupid return !result; } -EXPORT void llvm_module_to_string(Module* module, const char** module_pointer, size_t* module_length) +EXPORT BBLLVMString llvm_module_to_string(Module* module) { std::string buffer; raw_string_ostream stream(buffer); - module->print(stream, nullptr); + module->print(stream, 0); - stream_to_string(stream, module_pointer, module_length); + return stream_to_string(stream); +} + +EXPORT BBLLVMString llvm_default_target_triple() +{ + auto triple = llvm::sys::getDefaultTargetTriple(); + auto length = triple.length(); + + char* pointer = 0; + if (length) + { + pointer = new char[length]; + memcpy(pointer, triple.c_str(), length); + } + + return { pointer, length }; +} + +EXPORT BBLLVMString llvm_host_cpu_name() +{ + auto cpu = llvm::sys::getHostCPUName(); + return { cpu.data(), cpu.size() }; +} + +EXPORT BBLLVMString llvm_host_cpu_features() +{ + SubtargetFeatures Features; + for (const auto &[Feature, IsEnabled] : sys::getHostCPUFeatures()) + { + Features.AddFeature(Feature, IsEnabled); + } + + auto feature_string = Features.getString(); + auto length = feature_string.length(); + + char* result = 0; + if (length) + { + result = new char[length]; + memcpy(result, feature_string.c_str(), length); + } + + return { result, length }; +} + +enum class BBLLVMCodeModel : u8 +{ + none = 0, + tiny = 1, + small = 2, + kernel = 3, + medium = 4, + large = 5, +}; + +enum class BBLLVMRelocationModel : u8 +{ + default_relocation = 0, + static_relocation = 1, + pic = 2, + dynamic_no_pic = 3, + ropi = 4, + rwpi = 5, + ropi_rwpi = 6, +}; + +enum class BBLLVMCodeGenerationOptimizationLevel : u8 +{ + none = 0, // -O0 + less = 1, // -O1 + normal = 2, // -O2, -Os + aggressive = 3 // -O3 +}; + + // class TargetOptions { + // public: + // TargetOptions() + // : UnsafeFPMath(false), NoInfsFPMath(false), NoNaNsFPMath(false), + // NoTrappingFPMath(true), NoSignedZerosFPMath(false), + // ApproxFuncFPMath(false), EnableAIXExtendedAltivecABI(false), + // HonorSignDependentRoundingFPMathOption(false), NoZerosInBSS(false), + // GuaranteedTailCallOpt(false), StackSymbolOrdering(true), + // EnableFastISel(false), EnableGlobalISel(false), UseInitArray(false), + // DisableIntegratedAS(false), FunctionSections(false), + // DataSections(false), IgnoreXCOFFVisibility(false), + // XCOFFTracebackTable(true), UniqueSectionNames(true), + // UniqueBasicBlockSectionNames(false), SeparateNamedSections(false), + // TrapUnreachable(false), NoTrapAfterNoreturn(false), TLSSize(0), + // EmulatedTLS(false), EnableTLSDESC(false), EnableIPRA(false), + // EmitStackSizeSection(false), EnableMachineOutliner(false), + // EnableMachineFunctionSplitter(false), SupportsDefaultOutlining(false), + // EmitAddrsig(false), BBAddrMap(false), EmitCallSiteInfo(false), + // SupportsDebugEntryValues(false), EnableDebugEntryValues(false), + // ValueTrackingVariableLocations(false), ForceDwarfFrameSection(false), + // XRayFunctionIndex(true), DebugStrictDwarf(false), Hotpatch(false), + // PPCGenScalarMASSEntries(false), JMCInstrument(false), + // EnableCFIFixup(false), MisExpect(false), XCOFFReadOnlyPointers(false), + // FPDenormalMode(DenormalMode::IEEE, DenormalMode::IEEE) {} + // + // /// DisableFramePointerElim - This returns true if frame pointer elimination + // /// optimization should be disabled for the given machine function. + // bool DisableFramePointerElim(const MachineFunction &MF) const; + // + // /// FramePointerIsReserved - This returns true if the frame pointer must + // /// always either point to a new frame record or be un-modified in the given + // /// function. + // bool FramePointerIsReserved(const MachineFunction &MF) const; + // + // /// If greater than 0, override the default value of + // /// MCAsmInfo::BinutilsVersion. + // std::pair BinutilsVersion{0, 0}; + // + // /// UnsafeFPMath - This flag is enabled when the + // /// -enable-unsafe-fp-math flag is specified on the command line. When + // /// this flag is off (the default), the code generator is not allowed to + // /// produce results that are "less precise" than IEEE allows. This includes + // /// use of X86 instructions like FSIN and FCOS instead of libcalls. + // unsigned UnsafeFPMath : 1; + // + // /// NoInfsFPMath - This flag is enabled when the + // /// -enable-no-infs-fp-math flag is specified on the command line. When + // /// this flag is off (the default), the code generator is not allowed to + // /// assume the FP arithmetic arguments and results are never +-Infs. + // unsigned NoInfsFPMath : 1; + // + // /// NoNaNsFPMath - This flag is enabled when the + // /// -enable-no-nans-fp-math flag is specified on the command line. When + // /// this flag is off (the default), the code generator is not allowed to + // /// assume the FP arithmetic arguments and results are never NaNs. + // unsigned NoNaNsFPMath : 1; + // + // /// NoTrappingFPMath - This flag is enabled when the + // /// -enable-no-trapping-fp-math is specified on the command line. This + // /// specifies that there are no trap handlers to handle exceptions. + // unsigned NoTrappingFPMath : 1; + // + // /// NoSignedZerosFPMath - This flag is enabled when the + // /// -enable-no-signed-zeros-fp-math is specified on the command line. This + // /// specifies that optimizations are allowed to treat the sign of a zero + // /// argument or result as insignificant. + // unsigned NoSignedZerosFPMath : 1; + // + // /// ApproxFuncFPMath - This flag is enabled when the + // /// -enable-approx-func-fp-math is specified on the command line. This + // /// specifies that optimizations are allowed to substitute math functions + // /// with approximate calculations + // unsigned ApproxFuncFPMath : 1; + // + // /// EnableAIXExtendedAltivecABI - This flag returns true when -vec-extabi is + // /// specified. The code generator is then able to use both volatile and + // /// nonvolitle vector registers. When false, the code generator only uses + // /// volatile vector registers which is the default setting on AIX. + // unsigned EnableAIXExtendedAltivecABI : 1; + // + // /// HonorSignDependentRoundingFPMath - This returns true when the + // /// -enable-sign-dependent-rounding-fp-math is specified. If this returns + // /// false (the default), the code generator is allowed to assume that the + // /// rounding behavior is the default (round-to-zero for all floating point + // /// to integer conversions, and round-to-nearest for all other arithmetic + // /// truncations). If this is enabled (set to true), the code generator must + // /// assume that the rounding mode may dynamically change. + // unsigned HonorSignDependentRoundingFPMathOption : 1; + // bool HonorSignDependentRoundingFPMath() const; + // + // /// NoZerosInBSS - By default some codegens place zero-initialized data to + // /// .bss section. This flag disables such behaviour (necessary, e.g. for + // /// crt*.o compiling). + // unsigned NoZerosInBSS : 1; + // + // /// GuaranteedTailCallOpt - This flag is enabled when -tailcallopt is + // /// specified on the commandline. When the flag is on, participating targets + // /// will perform tail call optimization on all calls which use the fastcc + // /// calling convention and which satisfy certain target-independent + // /// criteria (being at the end of a function, having the same return type + // /// as their parent function, etc.), using an alternate ABI if necessary. + // unsigned GuaranteedTailCallOpt : 1; + // + // /// StackSymbolOrdering - When true, this will allow CodeGen to order + // /// the local stack symbols (for code size, code locality, or any other + // /// heuristics). When false, the local symbols are left in whatever order + // /// they were generated. Default is true. + // unsigned StackSymbolOrdering : 1; + // + // /// EnableFastISel - This flag enables fast-path instruction selection + // /// which trades away generated code quality in favor of reducing + // /// compile time. + // unsigned EnableFastISel : 1; + // + // /// EnableGlobalISel - This flag enables global instruction selection. + // unsigned EnableGlobalISel : 1; + // + // /// EnableGlobalISelAbort - Control abort behaviour when global instruction + // /// selection fails to lower/select an instruction. + // GlobalISelAbortMode GlobalISelAbort = GlobalISelAbortMode::Enable; + // + // /// Control when and how the Swift async frame pointer bit should + // /// be set. + // SwiftAsyncFramePointerMode SwiftAsyncFramePointer = + // SwiftAsyncFramePointerMode::Always; + // + // /// UseInitArray - Use .init_array instead of .ctors for static + // /// constructors. + // unsigned UseInitArray : 1; + // + // /// Disable the integrated assembler. + // unsigned DisableIntegratedAS : 1; + // + // /// Emit functions into separate sections. + // unsigned FunctionSections : 1; + // + // /// Emit data into separate sections. + // unsigned DataSections : 1; + // + // /// Do not emit visibility attribute for xcoff. + // unsigned IgnoreXCOFFVisibility : 1; + // + // /// Emit XCOFF traceback table. + // unsigned XCOFFTracebackTable : 1; + // + // unsigned UniqueSectionNames : 1; + // + // /// Use unique names for basic block sections. + // unsigned UniqueBasicBlockSectionNames : 1; + // + // /// Emit named sections with the same name into different sections. + // unsigned SeparateNamedSections : 1; + // + // /// Emit target-specific trap instruction for 'unreachable' IR instructions. + // unsigned TrapUnreachable : 1; + // + // /// Do not emit a trap instruction for 'unreachable' IR instructions behind + // /// noreturn calls, even if TrapUnreachable is true. + // unsigned NoTrapAfterNoreturn : 1; + // + // /// Bit size of immediate TLS offsets (0 == use the default). + // unsigned TLSSize : 8; + // + // /// EmulatedTLS - This flag enables emulated TLS model, using emutls + // /// function in the runtime library.. + // unsigned EmulatedTLS : 1; + // + // /// EnableTLSDESC - This flag enables TLS Descriptors. + // unsigned EnableTLSDESC : 1; + // + // /// This flag enables InterProcedural Register Allocation (IPRA). + // unsigned EnableIPRA : 1; + // + // /// Emit section containing metadata on function stack sizes. + // unsigned EmitStackSizeSection : 1; + // + // /// Enables the MachineOutliner pass. + // unsigned EnableMachineOutliner : 1; + // + // /// Enables the MachineFunctionSplitter pass. + // unsigned EnableMachineFunctionSplitter : 1; + // + // /// Set if the target supports default outlining behaviour. + // unsigned SupportsDefaultOutlining : 1; + // + // /// Emit address-significance table. + // unsigned EmitAddrsig : 1; + // + // // Emit the SHT_LLVM_BB_ADDR_MAP section containing basic block address + // // which can be used to map virtual addresses to machine basic blocks. + // unsigned BBAddrMap : 1; + // + // /// Emit basic blocks into separate sections. + // BasicBlockSection BBSections = BasicBlockSection::None; + // + // /// Memory Buffer that contains information on sampled basic blocks and used + // /// to selectively generate basic block sections. + // std::shared_ptr BBSectionsFuncListBuf; + // + // /// The flag enables call site info production. It is used only for debug + // /// info, and it is restricted only to optimized code. This can be used for + // /// something else, so that should be controlled in the frontend. + // unsigned EmitCallSiteInfo : 1; + // /// Set if the target supports the debug entry values by default. + // unsigned SupportsDebugEntryValues : 1; + // /// When set to true, the EnableDebugEntryValues option forces production + // /// of debug entry values even if the target does not officially support + // /// it. Useful for testing purposes only. This flag should never be checked + // /// directly, always use \ref ShouldEmitDebugEntryValues instead. + // unsigned EnableDebugEntryValues : 1; + // /// NOTE: There are targets that still do not support the debug entry values + // /// production. + // bool ShouldEmitDebugEntryValues() const; + // + // // When set to true, use experimental new debug variable location tracking, + // // which seeks to follow the values of variables rather than their location, + // // post isel. + // unsigned ValueTrackingVariableLocations : 1; + // + // /// Emit DWARF debug frame section. + // unsigned ForceDwarfFrameSection : 1; + // + // /// Emit XRay Function Index section + // unsigned XRayFunctionIndex : 1; + // + // /// When set to true, don't use DWARF extensions in later DWARF versions. + // /// By default, it is set to false. + // unsigned DebugStrictDwarf : 1; + // + // /// Emit the hotpatch flag in CodeView debug. + // unsigned Hotpatch : 1; + // + // /// Enables scalar MASS conversions + // unsigned PPCGenScalarMASSEntries : 1; + // + // /// Enable JustMyCode instrumentation. + // unsigned JMCInstrument : 1; + // + // /// Enable the CFIFixup pass. + // unsigned EnableCFIFixup : 1; + // + // /// When set to true, enable MisExpect Diagnostics + // /// By default, it is set to false + // unsigned MisExpect : 1; + // + // /// When set to true, const objects with relocatable address values are put + // /// into the RO data section. + // unsigned XCOFFReadOnlyPointers : 1; + // + // /// Name of the stack usage file (i.e., .su file) if user passes + // /// -fstack-usage. If empty, it can be implied that -fstack-usage is not + // /// passed on the command line. + // std::string StackUsageOutput; + // + // /// If greater than 0, override TargetLoweringBase::PrefLoopAlignment. + // unsigned LoopAlignment = 0; + // + // /// FloatABIType - This setting is set by -float-abi=xxx option is specfied + // /// on the command line. This setting may either be Default, Soft, or Hard. + // /// Default selects the target's default behavior. Soft selects the ABI for + // /// software floating point, but does not indicate that FP hardware may not + // /// be used. Such a combination is unfortunately popular (e.g. + // /// arm-apple-darwin). Hard presumes that the normal FP ABI is used. + // FloatABI::ABIType FloatABIType = FloatABI::Default; + // + // /// AllowFPOpFusion - This flag is set by the -fp-contract=xxx option. + // /// This controls the creation of fused FP ops that store intermediate + // /// results in higher precision than IEEE allows (E.g. FMAs). + // /// + // /// Fast mode - allows formation of fused FP ops whenever they're + // /// profitable. + // /// Standard mode - allow fusion only for 'blessed' FP ops. At present the + // /// only blessed op is the fmuladd intrinsic. In the future more blessed ops + // /// may be added. + // /// Strict mode - allow fusion only if/when it can be proven that the excess + // /// precision won't effect the result. + // /// + // /// Note: This option only controls formation of fused ops by the + // /// optimizers. Fused operations that are explicitly specified (e.g. FMA + // /// via the llvm.fma.* intrinsic) will always be honored, regardless of + // /// the value of this option. + // FPOpFusion::FPOpFusionMode AllowFPOpFusion = FPOpFusion::Standard; + // + // /// ThreadModel - This flag specifies the type of threading model to assume + // /// for things like atomics + // ThreadModel::Model ThreadModel = ThreadModel::POSIX; + // + // /// EABIVersion - This flag specifies the EABI version + // EABI EABIVersion = EABI::Default; + // + // /// Which debugger to tune for. + // DebuggerKind DebuggerTuning = DebuggerKind::Default; + // + // private: + // /// Flushing mode to assume in default FP environment. + // DenormalMode FPDenormalMode; + // + // /// Flushing mode to assume in default FP environment, for float/vector of + // /// float. + // DenormalMode FP32DenormalMode; + // + // public: + // void setFPDenormalMode(DenormalMode Mode) { + // FPDenormalMode = Mode; + // } + // + // void setFP32DenormalMode(DenormalMode Mode) { + // FP32DenormalMode = Mode; + // } + // + // DenormalMode getRawFPDenormalMode() const { + // return FPDenormalMode; + // } + // + // DenormalMode getRawFP32DenormalMode() const { + // return FP32DenormalMode; + // } + // + // DenormalMode getDenormalMode(const fltSemantics &FPType) const; + // + // /// What exception model to use + // ExceptionHandling ExceptionModel = ExceptionHandling::None; + // + // /// Machine level options. + // MCTargetOptions MCOptions; + // + // /// Stores the filename/path of the final .o/.obj file, to be written in the + // /// debug information. This is used for emitting the CodeView S_OBJNAME + // /// record. + // std::string ObjectFilenameForDebug; + // }; + // namespace FloatABI { + // enum ABIType { + // Default, // Target-specific (either soft or hard depending on triple, etc). + // Soft, // Soft float. + // Hard // Hard float. + // }; + // } + // + // namespace FPOpFusion { + // enum FPOpFusionMode { + // Fast, // Enable fusion of FP ops wherever it's profitable. + // Standard, // Only allow fusion of 'blessed' ops (currently just fmuladd). + // Strict // Never fuse FP-ops. + // }; + // } + // + // namespace JumpTable { + // enum JumpTableType { + // Single, // Use a single table for all indirect jumptable calls. + // Arity, // Use one table per number of function parameters. + // Simplified, // Use one table per function type, with types projected + // // into 4 types: pointer to non-function, struct, + // // primitive, and function pointer. + // Full // Use one table per unique function type + // }; + // } + // + // namespace ThreadModel { + // enum Model { + // POSIX, // POSIX Threads + // Single // Single Threaded Environment + // }; + // } + // + // enum class BasicBlockSection { + // All, // Use Basic Block Sections for all basic blocks. A section + // // for every basic block can significantly bloat object file sizes. + // List, // Get list of functions & BBs from a file. Selectively enables + // // basic block sections for a subset of basic blocks which can be + // // used to control object size bloats from creating sections. + // Labels, // Do not use Basic Block Sections but label basic blocks. This + // // is useful when associating profile counts from virtual addresses + // // to basic blocks. + // Preset, // Similar to list but the blocks are identified by passes which + // // seek to use Basic Block Sections, e.g. MachineFunctionSplitter. + // // This option cannot be set via the command line. + // None // Do not use Basic Block Sections. + // }; + // + // enum class EABI { + // Unknown, + // Default, // Default means not specified + // EABI4, // Target-specific (either 4, 5 or gnu depending on triple). + // EABI5, + // GNU + // }; + // + // /// Identify a debugger for "tuning" the debug info. + // /// + // /// The "debugger tuning" concept allows us to present a more intuitive + // /// interface that unpacks into different sets of defaults for the various + // /// individual feature-flag settings, that suit the preferences of the + // /// various debuggers. However, it's worth remembering that debuggers are + // /// not the only consumers of debug info, and some variations in DWARF might + // /// better be treated as target/platform issues. Fundamentally, + // /// o if the feature is useful (or not) to a particular debugger, regardless + // /// of the target, that's a tuning decision; + // /// o if the feature is useful (or not) on a particular platform, regardless + // /// of the debugger, that's a target decision. + // /// It's not impossible to see both factors in some specific case. + // enum class DebuggerKind { + // Default, ///< No specific tuning requested. + // GDB, ///< Tune debug info for gdb. + // LLDB, ///< Tune debug info for lldb. + // SCE, ///< Tune debug info for SCE targets (e.g. PS4). + // DBX ///< Tune debug info for dbx. + // }; + // + // /// Enable abort calls when global instruction selection fails to lower/select + // /// an instruction. + // enum class GlobalISelAbortMode { + // Disable, // Disable the abort. + // Enable, // Enable the abort. + // DisableWithDiag // Disable the abort but emit a diagnostic on failure. + // }; + // + // /// Indicates when and how the Swift async frame pointer bit should be set. + // enum class SwiftAsyncFramePointerMode { + // /// Determine whether to set the bit statically or dynamically based + // /// on the deployment target. + // DeploymentBased, + // /// Always set the bit. + // Always, + // /// Never set the bit. + // Never, + // }; + // + // /// \brief Enumeration value for AMDGPU code object version, which is the + // /// code object version times 100. + // enum CodeObjectVersionKind { + // COV_None, + // COV_2 = 200, // Unsupported. + // COV_3 = 300, // Unsupported. + // COV_4 = 400, + // COV_5 = 500, + // COV_6 = 600, + // }; +struct BBLLVMTargetOptions +{ +}; + +struct BBLLVMTargetMachineCreate +{ + BBLLVMTargetOptions target_options; + BBLLVMString target_triple; + BBLLVMString cpu_model; + BBLLVMString cpu_features; + BBLLVMRelocationModel relocation_model; + BBLLVMCodeModel code_model; + BBLLVMCodeGenerationOptimizationLevel optimization_level; + bool jit; + u32 reserved; +}; + +EXPORT TargetMachine* llvm_create_target_machine(const BBLLVMTargetMachineCreate& create, BBLLVMString* error_message) +{ + std::string error_message_string; + const Target* target = TargetRegistry::lookupTarget(create.target_triple.string_ref(), error_message_string); + + TargetMachine* target_machine; + + if (target) + { + std::optional code_model; + switch (create.code_model) + { + case BBLLVMCodeModel::none: code_model = std::nullopt; break; + case BBLLVMCodeModel::tiny: code_model = CodeModel::Tiny; break; + case BBLLVMCodeModel::small: code_model = CodeModel::Small; break; + case BBLLVMCodeModel::kernel: code_model = CodeModel::Kernel; break; + case BBLLVMCodeModel::medium: code_model = CodeModel::Medium; break; + case BBLLVMCodeModel::large: code_model = CodeModel::Large; break; + } + + std::optional relocation_model; + + switch (create.relocation_model) + { + case BBLLVMRelocationModel::default_relocation: relocation_model = std::nullopt; break; + case BBLLVMRelocationModel::static_relocation: relocation_model = Reloc::Static; break; + case BBLLVMRelocationModel::pic: relocation_model = Reloc::PIC_; break; + case BBLLVMRelocationModel::dynamic_no_pic: relocation_model = Reloc::DynamicNoPIC; break; + case BBLLVMRelocationModel::ropi: relocation_model = Reloc::ROPI; break; + case BBLLVMRelocationModel::rwpi: relocation_model = Reloc::RWPI; break; + case BBLLVMRelocationModel::ropi_rwpi: relocation_model = Reloc::ROPI_RWPI; break; + } + + CodeGenOptLevel optimization_level; + switch (create.optimization_level) + { + case BBLLVMCodeGenerationOptimizationLevel::none: optimization_level = CodeGenOptLevel::None; break; + case BBLLVMCodeGenerationOptimizationLevel::less: optimization_level = CodeGenOptLevel::Less; break; + case BBLLVMCodeGenerationOptimizationLevel::normal: optimization_level = CodeGenOptLevel::Default; break; + case BBLLVMCodeGenerationOptimizationLevel::aggressive: optimization_level = CodeGenOptLevel::Aggressive; break; + } + + TargetOptions target_options; + target_machine = target->createTargetMachine(create.target_triple.string_ref(), create.cpu_model.string_ref(), create.cpu_features.string_ref(), target_options, relocation_model, code_model, optimization_level, create.jit); + } + else + { + auto length = error_message_string.length(); + char* result = new char[length]; + memcpy(result, error_message_string.c_str(), length); + + *error_message = { result, length }; + } + + return target_machine; } diff --git a/src/llvm_api.zig b/src/llvm_api.zig index b9b7f2b..38ad14c 100644 --- a/src/llvm_api.zig +++ b/src/llvm_api.zig @@ -2,19 +2,18 @@ const llvm = @import("LLVM.zig"); const Bool = c_int; -pub extern fn llvm_context_create_module(context: *llvm.Context, name_pointer: [*]const u8, name_length: usize) *llvm.Module; +pub extern fn llvm_context_create_module(context: *llvm.Context, name: llvm.String) *llvm.Module; pub extern fn LLVMContextCreate() *llvm.Context; pub extern fn LLVMCreateBuilderInContext(context: *llvm.Context) *llvm.Builder; // Module -pub extern fn llvm_module_create_function(module: *llvm.Module, function_type: *llvm.Type.Function, linkage_type: llvm.LinkageType, address_space: c_uint, name_pointer: [*]const u8, name_length: usize) *llvm.Function; -pub extern fn llvm_context_create_basic_block(context: *llvm.Context, name_pointer: [*]const u8, name_length: usize, parent: *llvm.Function) *llvm.BasicBlock; +pub extern fn llvm_module_create_function(module: *llvm.Module, function_type: *llvm.Type.Function, linkage_type: llvm.LinkageType, address_space: c_uint, name: llvm.String) *llvm.Function; +pub extern fn llvm_context_create_basic_block(context: *llvm.Context, name: llvm.String, parent: *llvm.Function) *llvm.BasicBlock; -pub extern fn llvm_function_verify(function: *llvm.Function, message_pointer: *[*]const u8, message_length: *usize) bool; -pub extern fn llvm_module_verify(module: *llvm.Module, message_pointer: *[*]const u8, message_length: *usize) bool; +pub extern fn llvm_function_verify(function: *llvm.Function, error_message: *llvm.String) bool; +pub extern fn llvm_module_verify(module: *llvm.Module, error_message: *llvm.String) bool; -pub extern fn llvm_module_to_string(module: *llvm.Module, module_pointer: *[*]const u8, module_length: *usize) void; -pub extern fn LLVMPrintModuleToString(module: *llvm.Module) [*:0]const u8; +pub extern fn llvm_module_to_string(module: *llvm.Module) llvm.String; // Builder API pub extern fn LLVMPositionBuilderAtEnd(builder: *llvm.Builder, basic_block: *llvm.BasicBlock) void; @@ -45,7 +44,7 @@ pub extern fn LLVMCountParamTypes(function_type: *llvm.Type.Function) c_uint; pub extern fn LLVMGetParamTypes(function_type: *llvm.Type.Function, types: [*]*llvm.Type) void; // Types: struct -pub extern fn llvm_context_create_struct_type(context: *llvm.Context, element_types_pointer: [*]const *llvm.Type, element_type_count: usize, name_pointer: [*]const u8, name_length: usize, is_packed: bool) *llvm.Type.Struct; +pub extern fn llvm_context_create_struct_type(context: *llvm.Context, element_types_pointer: [*]const *llvm.Type, element_type_count: usize, name: llvm.String, is_packed: bool) *llvm.Type.Struct; pub extern fn llvm_context_get_struct_type(context: *llvm.Context, element_types_pointer: [*]const *llvm.Type, element_type_count: usize, is_packed: bool) *llvm.Type.Struct; // Types: arrays @@ -63,9 +62,14 @@ pub extern fn LLVMConstInt(type: *llvm.Type.Integer, value: c_ulonglong, sign_ex // Debug info API pub extern fn LLVMCreateDIBuilder(module: *llvm.Module) *llvm.DI.Builder; -pub extern fn LLVMDIBuilderCreateFile(builder: *llvm.DI.Builder, file_name_pointer: [*]const u8, file_name_length: usize, directory_name_pointer: [*]const u8, directory_name_length: usize) *llvm.DI.File; -pub extern fn LLVMDIBuilderCreateCompileUnit(builder: *llvm.DI.Builder, language: llvm.DwarfSourceLanguage, file: *llvm.DI.File, producer_name_pointer: [*]const u8, producer_name_length: usize, optimized: Bool, flags_pointer: [*]const u8, flags_length: usize, runtime_version: c_uint, split_name_pointer: [*]const u8, split_name_length: usize, dwarf_emission_kind: llvm.DwarfEmissionKind, debug_with_offset_id: c_uint, split_debug_inlining: Bool, debug_info_for_profiling: Bool, sysroot_pointer: [*]const u8, sysroot_length: usize, sdk_pointer: [*]const u8, sdk_length: usize) *llvm.DI.CompileUnit; -pub extern fn LLVMDIBuilderCreateFunction(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name_pointer: [*]const u8, name_length: usize, linkage_name_pointer: [*]const u8, linkage_name_length: usize, file: *llvm.DI.File, line_number: c_uint, type: *llvm.DI.Type.Subroutine, local_to_unit: Bool, is_definition: Bool, scope_line: c_uint, flags: llvm.DI.Flags, is_optimized: Bool) *llvm.DI.Subprogram; +pub extern fn LLVMDIBuilderCreateFile(builder: *llvm.DI.Builder, file_name: llvm.String, directory_name: llvm.String) *llvm.DI.File; +pub extern fn LLVMDIBuilderCreateCompileUnit(builder: *llvm.DI.Builder, language: llvm.DwarfSourceLanguage, file: *llvm.DI.File, producer_name: llvm.String, optimized: Bool, flags: llvm.String, runtime_version: c_uint, split_name: llvm.String, dwarf_emission_kind: llvm.DwarfEmissionKind, debug_with_offset_id: c_uint, split_debug_inlining: Bool, debug_info_for_profiling: Bool, sysroot: llvm.String, sdk: llvm.String) *llvm.DI.CompileUnit; +pub extern fn LLVMDIBuilderCreateFunction(builder: *llvm.DI.Builder, scope: *llvm.DI.Scope, name: llvm.String, linkage_name: llvm.String, file: *llvm.DI.File, line_number: c_uint, type: *llvm.DI.Type.Subroutine, local_to_unit: Bool, is_definition: Bool, scope_line: c_uint, flags: llvm.DI.Flags, is_optimized: Bool) *llvm.DI.Subprogram; + +// Target +pub extern fn llvm_default_target_triple() llvm.String; +pub extern fn llvm_host_cpu_name() llvm.String; +pub extern fn llvm_host_cpu_features() llvm.String; pub fn get_initializer(comptime llvm_arch: llvm.Architecture) type { const arch_name = @tagName(llvm_arch);