Implement function attributes

This commit is contained in:
David Gonzalez Martin 2025-02-27 21:29:19 -06:00
parent 1599a78d03
commit b3eee4ae99
4 changed files with 396 additions and 70 deletions

View File

@ -165,6 +165,7 @@ const LLVM = struct {
const llvm = b.createModule(.{
.target = target,
.optimize = optimize,
.sanitize_c = false,
});
llvm.addLibraryPath(.{ .cwd_relative = llvm_lib_dir });
@ -324,6 +325,7 @@ pub fn build(b: *std.Build) !void {
.target = target,
.optimize = optimize,
.link_libc = true,
.sanitize_c = false,
});
const configuration = b.addOptions();
configuration.addOptionPath("c_abi_object_path", c_abi.getEmittedBin());

View File

@ -26,6 +26,24 @@ pub const String = extern struct {
}
};
pub const Intrinsic = enum {
pub const Id = enum(c_uint) {
_,
};
};
pub const Attribute = opaque {
pub const Index = enum(c_uint) {
@"return" = 0,
function = 0xffff_ffff,
_,
};
pub const Kind = enum(c_uint) {
_,
};
};
pub const CodeModel = enum(u8) {
none = 0,
tiny = 1,
@ -488,12 +506,6 @@ const targets = [@typeInfo(Architecture).@"enum".fields.len]type{
api.get_initializer(.X86),
};
pub const Intrinsic = enum {
pub const Id = enum(c_uint) {
_,
};
};
pub const Context = opaque {
pub const create = api.LLVMContextCreate;
@ -528,6 +540,13 @@ pub const Context = opaque {
pub fn get_intrinsic_type(context: *Context, intrinsic_id: Intrinsic.Id, parameter_types: []const *Type) *Type.Function {
return api.LLVMIntrinsicGetType(context, intrinsic_id, parameter_types.ptr, parameter_types.len);
}
pub fn create_string_attribute(context: *Context, attribute_name: []const u8, attribute_value: []const u8) *Attribute {
return api.LLVMCreateStringAttribute(context, attribute_name.ptr, @intCast(attribute_name.len), attribute_value.ptr, @intCast(attribute_value.len));
}
pub const create_enum_attribute = api.LLVMCreateEnumAttribute;
pub const create_type_attribute = api.LLVMCreateTypeAttribute;
};
pub const BasicBlock = opaque {
@ -741,6 +760,8 @@ pub const Function = opaque {
pub const get_calling_convention = api.LLVMGetFunctionCallConv;
pub const get_arguments = api.LLVMGetParams;
pub const add_attribute = api.LLVMAddAttributeAtIndex;
};
pub const Constant = opaque {
@ -771,6 +792,8 @@ pub const Value = opaque {
pub const get_type = api.LLVMTypeOf;
pub const get_kind = api.LLVMGetValueKind;
pub const is_call_instruction = api.LLVMIsACallInst;
pub fn is_constant(value: *Value) bool {
return api.LLVMIsConstant(value) != 0;
}
@ -794,8 +817,8 @@ pub const Value = opaque {
const kind = value.get_kind();
switch (kind) {
.Instruction => {
const instruction = value.to_instruction();
return instruction.get_calling_convention();
const call = value.to_instruction().to_call();
return call.get_calling_convention();
},
.Function => {
const function = value.to_function();
@ -842,8 +865,18 @@ pub const Value = opaque {
};
pub const Instruction = opaque {
pub const set_calling_convention = api.LLVMSetInstructionCallConv;
pub const get_calling_convention = api.LLVMGetInstructionCallConv;
pub fn to_value(instruction: *Instruction) *Value {
return @ptrCast(instruction);
}
pub fn to_call(instruction: *Instruction) *Instruction.Call {
assert(instruction.to_value().is_call_instruction() != null);
return @ptrCast(instruction);
}
pub const Call = opaque {
pub const set_calling_convention = api.LLVMSetInstructionCallConv;
pub const get_calling_convention = api.LLVMGetInstructionCallConv;
pub const add_attribute = api.LLVMAddCallSiteAttribute;
};
};
pub const DI = struct {
@ -1236,6 +1269,10 @@ pub fn lookup_intrinsic_id(name: []const u8) Intrinsic.Id {
return api.LLVMLookupIntrinsicID(name.ptr, name.len);
}
pub fn lookup_attribute_kind(name: []const u8) Attribute.Kind {
return api.LLVMGetEnumAttributeKindForName(name.ptr, name.len);
}
pub const IntPredicate = enum(c_int) {
eq = 32,
ne,

View File

@ -115,9 +115,34 @@ const Module = struct {
global_scope: *llvm.DI.Scope,
file: *llvm.DI.File,
pointer_type: *llvm.Type,
// intrinsics: struct {
// trap:
// },
intrinsic_table: IntrinsicTable,
attribute_table: AttributeTable,
attribute_kind_table: AttributeKindTable,
const IntrinsicTable = struct {
trap: llvm.Intrinsic.Id,
};
const AttributeTable = struct {
frame_pointer_all: *llvm.Attribute,
ssp: *llvm.Attribute,
@"stack-protector-buffer-size": *llvm.Attribute,
@"no-trapping-math": *llvm.Attribute,
alwaysinline: *llvm.Attribute,
@"noinline": *llvm.Attribute,
noreturn: *llvm.Attribute,
nounwind: *llvm.Attribute,
naked: *llvm.Attribute,
signext: *llvm.Attribute,
zeroext: *llvm.Attribute,
inreg: *llvm.Attribute,
@"noalias": *llvm.Attribute,
};
const AttributeKindTable = struct {
byval: llvm.Attribute.Kind,
sret: llvm.Attribute.Kind,
};
};
pub fn get_infer_or_ignore_value(module: *Module) *Value {
@ -179,6 +204,28 @@ const Module = struct {
.context = context,
.builder = context.create_builder(),
.pointer_type = context.get_pointer_type(default_address_space).to_type(),
.intrinsic_table = .{
.trap = llvm.lookup_intrinsic_id("llvm.trap"),
},
.attribute_table = .{
.frame_pointer_all = context.create_string_attribute("frame-pointer", "all"),
.ssp = context.create_enum_attribute(llvm.lookup_attribute_kind("ssp"), 0),
.@"stack-protector-buffer-size" = context.create_string_attribute("stack-protector-buffer-size", "8"),
.@"no-trapping-math" = context.create_string_attribute("no-trapping-math", "true"),
.@"noinline" = context.create_enum_attribute(llvm.lookup_attribute_kind("noinline"), 0),
.alwaysinline = context.create_enum_attribute(llvm.lookup_attribute_kind("alwaysinline"), 0),
.noreturn = context.create_enum_attribute(llvm.lookup_attribute_kind("noreturn"), 0),
.nounwind = context.create_enum_attribute(llvm.lookup_attribute_kind("nounwind"), 0),
.naked = context.create_enum_attribute(llvm.lookup_attribute_kind("naked"), 0),
.signext = context.create_enum_attribute(llvm.lookup_attribute_kind("signext"), 0),
.zeroext = context.create_enum_attribute(llvm.lookup_attribute_kind("zeroext"), 0),
.inreg = context.create_enum_attribute(llvm.lookup_attribute_kind("inreg"), 0),
.@"noalias" = context.create_enum_attribute(llvm.lookup_attribute_kind("noalias"), 0),
},
.attribute_kind_table = .{
.byval = llvm.lookup_attribute_kind("byval"),
.sret = llvm.lookup_attribute_kind("sret"),
},
},
};
@ -302,11 +349,52 @@ const Module = struct {
}
};
const AttributeContainerType = enum {
call,
function,
};
fn llvm_add_function_attribute(value: *llvm.Value, attribute: *llvm.Attribute, container_type: AttributeContainerType) void {
switch (container_type) {
.call => {
const call = value.is_call_instruction() orelse unreachable;
call.add_attribute(.function, attribute);
},
.function => {
const function = value.to_function();
function.add_attribute(.function, attribute);
},
}
}
fn llvm_add_argument_attribute(value: *llvm.Value, attribute: *llvm.Attribute, index: c_uint, container_type: AttributeContainerType) void {
switch (container_type) {
.call => {
const call = value.is_call_instruction() orelse unreachable;
call.add_attribute(@enumFromInt(index), attribute);
},
.function => {
const function = value.to_function();
function.add_attribute(@enumFromInt(index), attribute);
},
}
}
pub const Function = struct {
current_basic_block: *llvm.BasicBlock,
current_scope: *llvm.DI.Scope,
attributes: Attributes,
locals: Variable.Array = .{},
arguments: Variable.Array = .{},
const Attributes = struct {
inline_behavior: enum {
default,
always_inline,
no_inline,
} = .default,
naked: bool = false,
};
};
pub const ConstantInteger = struct {
@ -363,6 +451,7 @@ const FunctionType = struct {
semantic_argument_types: [*]const *Type,
argument_type_abis: [*]const Abi.Information,
abi_argument_types: [*]const *Type,
abi_return_type: *Type,
semantic_argument_count: u32,
abi_argument_count: u32,
calling_convention: CallingConvention,
@ -787,19 +876,71 @@ const Converter = struct {
}
}
fn emit_direct_coerce(module: *Module, ty: *Type, original_value: *Value) *llvm.Value {
const source_type = original_value.type;
const alloca = module.llvm.builder.create_alloca(source_type.llvm.handle, "");
_ = module.llvm.builder.create_store(original_value.llvm, alloca);
const target_type = ty;
const target_size = ty.get_byte_size();
const target_alignment = ty.get_byte_alignment();
const source_size = source_type.get_byte_size();
const source_alignment = source_type.get_byte_alignment();
const target_is_scalable_vector_type = false;
const source_is_scalable_vector_type = false;
if (source_size >= target_size and !source_is_scalable_vector_type and !target_is_scalable_vector_type) {
_ = source_alignment;
_ = target_alignment;
return module.llvm.builder.create_load(target_type.llvm.handle, alloca);
} else {
@trap();
// const alignment = @max(target_alignment, source_alignment);
// const temporal = emit_local_symbol(analyzer, thread, .{
// .name = 0,
// .initial_value = null,
// .type = args.coerced_type,
// .line = 0,
// .column = 0,
// });
// emit_memcpy(analyzer, thread, .{
// .destination = &temporal.instruction.value,
// .source = &local.instruction.value,
// .destination_alignment = .{
// .alignment = alignment,
// },
// .source_alignment = .{
// .alignment = source_alignment,
// },
// .size = source_size,
// .line = 0,
// .column = 0,
// .scope = analyzer.current_scope,
// });
//
// const load = emit_load(analyzer, thread, .{
// .value = &temporal.instruction.value,
// .type = args.coerced_type,
// .line = 0,
// .column = 0,
// .scope = analyzer.current_scope,
// });
// return &load.instruction.value;
}
}
fn parse_call(noalias converter: *Converter, noalias module: *Module, callable: *Value) *Value {
var llvm_abi_argument_value_buffer: [64]*llvm.Value = undefined;
const function_type = callable.type;
const calling_convention = function_type.bb.function.calling_convention;
const function_type = &callable.type.bb.function;
const calling_convention = function_type.calling_convention;
_ = calling_convention;
const llvm_indirect_return_value: ?*Value = switch (function_type.bb.function.return_type_abi.kind) {
const llvm_indirect_return_value: ?*Value = switch (function_type.return_type_abi.kind) {
.indirect => @trap(),
else => null,
};
var semantic_argument_count: usize = 0;
var abi_argument_count: usize = 0;
const function_semantic_argument_count = function_type.bb.function.semantic_argument_count;
const function_semantic_argument_count = function_type.semantic_argument_count;
while (true) : (semantic_argument_count += 1) {
converter.skip_space();
@ -813,36 +954,87 @@ const Converter = struct {
converter.report_error();
}
const semantic_argument_value = converter.parse_value(module, function_type.bb.function.semantic_argument_types[semantic_argument_index], .value);
const semantic_argument_value = converter.parse_value(module, function_type.semantic_argument_types[semantic_argument_index], .value);
const argument_abi = function_type.bb.function.argument_type_abis[semantic_argument_index];
const argument_abi = function_type.argument_type_abis[semantic_argument_index];
switch (argument_abi.kind) {
.direct => {
llvm_abi_argument_value_buffer[abi_argument_count] = semantic_argument_value.llvm;
abi_argument_count += 1;
},
else => @trap(),
.ignore => unreachable,
.direct_pair => unreachable,
.direct_coerce => |coerced_type| {
const v = emit_direct_coerce(module, coerced_type, semantic_argument_value);
llvm_abi_argument_value_buffer[abi_argument_count] = v;
abi_argument_count += 1;
},
.direct_coerce_int => unreachable,
.expand_coerce => unreachable,
.direct_split_struct_i32 => unreachable,
.indirect => unreachable,
.expand => unreachable,
}
}
assert(abi_argument_count == function_type.bb.function.abi_argument_count);
assert(abi_argument_count == function_type.abi_argument_count);
const llvm_abi_argument_values = llvm_abi_argument_value_buffer[0..abi_argument_count];
const llvm_call = module.llvm.builder.create_call(callable.type.llvm.handle.to_function(), callable.llvm, llvm_abi_argument_values);
llvm_call.to_instruction().set_calling_convention(callable.llvm.get_calling_convention());
llvm_call.to_instruction().to_call().set_calling_convention(callable.llvm.get_calling_convention());
llvm_emit_function_attributes(module, llvm_call, function_type, Function.Attributes{}, .call);
for (function_type.get_argument_type_abis()) |argument_type_abi| {
if (argument_type_abi.attributes.zero_extend) {
llvm_add_argument_attribute(llvm_call, module.llvm.attribute_table.zeroext, argument_type_abi.indices[0], .call);
}
if (argument_type_abi.attributes.sign_extend) {
llvm_add_argument_attribute(llvm_call, module.llvm.attribute_table.signext, argument_type_abi.indices[0], .call);
}
switch (argument_type_abi.kind) {
.indirect => |indirect| {
if (argument_type_abi.attributes.by_value) {
const by_value_attribute = module.llvm.context.create_type_attribute(module.llvm.attribute_kind_table.byval, indirect.type.llvm.handle);
llvm_add_argument_attribute(llvm_call, by_value_attribute, argument_type_abi.indices[0], .call);
}
// TODO: alignment
},
else => {},
}
}
var llvm_value = llvm_call;
var return_type = function_type.abi_return_type;
_ = &return_type;
_ = &llvm_value;
switch (function_type.return_type_abi.kind) {
.expand, .direct_split_struct_i32 => unreachable,
.ignore => {},
.indirect => {
@trap();
},
.direct => {},
else => @trap(),
}
if (llvm_indirect_return_value) |indirect_value| {
_ = indirect_value;
@trap();
} else {
const call = module.values.add();
call.* = .{
.llvm = llvm_call,
.type = function_type.bb.function.semantic_return_type,
const result = module.values.add();
result.* = .{
.llvm = llvm_value,
.type = return_type,
.bb = .instruction,
};
return call;
return result;
}
}
@ -1334,11 +1526,11 @@ const Converter = struct {
// TODO: lookup in advance
const intrinsic_id = llvm.lookup_intrinsic_id("llvm.trap");
const parameter_types: []const *llvm.Type = &.{};
const parameter_values: []const *llvm.Value = &.{};
const intrinsic_function = module.llvm.handle.get_intrinsic_declaration(intrinsic_id, parameter_types);
const intrinsic_function_type = module.llvm.context.get_intrinsic_type(intrinsic_id, parameter_types);
const llvm_call = module.llvm.builder.create_call(intrinsic_function_type, intrinsic_function, parameter_values);
const argument_types: []const *llvm.Type = &.{};
const argument_values: []const *llvm.Value = &.{};
const intrinsic_function = module.llvm.handle.get_intrinsic_declaration(intrinsic_id, argument_types);
const intrinsic_function_type = module.llvm.context.get_intrinsic_type(intrinsic_id, argument_types);
const llvm_call = module.llvm.builder.create_call(intrinsic_function_type, intrinsic_function, argument_values);
_ = module.llvm.builder.create_unreachable();
const value = module.values.add();
@ -2189,6 +2381,89 @@ const ConvertOptions = struct {
target: Target,
};
fn llvm_emit_function_attributes(module: *Module, value: *llvm.Value, function_type: *FunctionType, function_attributes: Function.Attributes, container_type: AttributeContainerType) void {
const enable_frame_pointer = true;
if (enable_frame_pointer) {
llvm_add_function_attribute(value, module.llvm.attribute_table.frame_pointer_all, container_type);
llvm_add_function_attribute(value, module.llvm.attribute_table.ssp, container_type);
}
llvm_add_function_attribute(value, module.llvm.attribute_table.@"stack-protector-buffer-size", container_type);
llvm_add_function_attribute(value, module.llvm.attribute_table.@"no-trapping-math", container_type);
llvm_add_function_attribute(value, module.llvm.attribute_table.nounwind, container_type);
switch (function_attributes.inline_behavior) {
.default => {},
.no_inline => llvm_add_function_attribute(value, module.llvm.attribute_table.@"noinline", container_type),
.always_inline => llvm_add_function_attribute(value, module.llvm.attribute_table.alwaysinline, container_type),
}
if (function_attributes.naked) {
llvm_add_function_attribute(value, module.llvm.attribute_table.naked, container_type);
}
if (function_type.abi_return_type == module.noreturn_type) {
llvm_add_function_attribute(value, module.llvm.attribute_table.noreturn, container_type);
}
}
fn llvm_emit_function_site_argument_attributes(noalias module: *Module, function: *llvm.Value, argument_abi: Abi.Information, is_return: bool) void {
// assert(argument_abi.indices[1] == argument_abi.indices[0] or argument_abi.kind == .direct_pair or argument_abi.kind == .direct or argument_abi.kind == .ignore or argument_abi.kind == .expand or argument_abi.kind == .direct_coerce or argument_abi.kind == .direct_coerce_int or argument_abi.kind == .expand_coerce or argument_abi.kind == .direct_split_struct_i32);
if (argument_abi.attributes.zero_extend) {
llvm_add_argument_attribute(function, module.llvm.attribute_table.zeroext, argument_abi.indices[0], .function);
}
if (argument_abi.attributes.sign_extend) {
llvm_add_argument_attribute(function, module.llvm.attribute_table.signext, argument_abi.indices[0], .function);
}
if (argument_abi.attributes.by_reg) {
llvm_add_argument_attribute(function, module.llvm.attribute_table.inreg, argument_abi.indices[0], .function);
}
switch (argument_abi.kind) {
.direct => {},
.indirect => |indirect| switch (is_return) {
true => {
const sret_attribute = module.llvm.context.create_type_attribute(module.llvm.attribute_kind_table.sret, indirect.type.llvm.handle);
llvm_add_argument_attribute(function, sret_attribute, argument_abi.indices[0], .function);
llvm_add_argument_attribute(function, module.llvm.attribute_table.@"noalias", argument_abi.indices[0], .function);
// TODO: alignment
},
false => {
if (argument_abi.attributes.by_value) {
const by_value_attribute = module.llvm.context.create_type_attribute(module.llvm.attribute_kind_table.byval, indirect.type.llvm.handle);
llvm_add_argument_attribute(function, by_value_attribute, argument_abi.indices[0], .function);
}
// TODO: alignment
},
},
else => {},
}
}
fn llvm_emit_function_site_attributes(module: *Module, value: *Value) void {
const llvm_value = value.llvm;
const function_type = &value.type.bb.function;
const function_attributes = switch (value.bb) {
.function => value.bb.function.attributes,
else => Function.Attributes{},
};
llvm_emit_function_attributes(module, llvm_value, function_type, function_attributes, .function);
llvm_emit_function_site_argument_attributes(module, llvm_value, function_type.return_type_abi, true);
for (function_type.get_argument_type_abis()) |argument_type_abi| {
llvm_emit_function_site_argument_attributes(module, llvm_value, argument_type_abi, false);
}
}
pub noinline fn convert(options: ConvertOptions) void {
var converter = Converter{
.content = options.content,
@ -2266,6 +2541,8 @@ pub noinline fn convert(options: ConvertOptions) void {
switch (global_kind) {
.@"fn" => {
var calling_convention = CallingConvention.unknown;
var function_attributes = Function.Attributes{};
_ = &function_attributes;
if (converter.consume_character_if_match(left_bracket)) {
while (converter.offset < converter.content.len) {
@ -2478,8 +2755,6 @@ pub noinline fn convert(options: ConvertOptions) void {
assert(type_classes[1] != .memory or type_classes[0] == .memory);
assert(type_classes[1] != .sseup or type_classes[0] == .sse);
_ = &needed_registers; // autofix
const result_type = switch (type_classes[0]) {
.integer => b: {
needed_registers.gpr += 1;
@ -2493,7 +2768,7 @@ pub noinline fn convert(options: ConvertOptions) void {
break :ata .{
.kind = .{
.direct_coerce = semantic_argument_type,
.direct_coerce = result_type,
},
.attributes = .{
.sign_extend = signed,
@ -2501,6 +2776,7 @@ pub noinline fn convert(options: ConvertOptions) void {
},
};
}
break :b result_type;
},
.memory => break :ata Abi.SystemV.indirect_argument(semantic_argument_type, available_registers.gpr),
@ -2670,9 +2946,7 @@ pub noinline fn convert(options: ConvertOptions) void {
},
.type = llvm_function_type,
});
llvm_handle.set_calling_convention(calling_convention.to_llvm());
const global = module.globals.add();
var subroutine_type: *llvm.DI.Type.Subroutine = undefined;
const current_scope: *llvm.DI.Scope = if (module.llvm.di_builder) |di_builder| blk: {
const subroutine_type_flags = llvm.DI.Flags{};
@ -2708,49 +2982,49 @@ pub noinline fn convert(options: ConvertOptions) void {
.semantic_argument_count = semantic_argument_count,
.abi_argument_count = @intCast(abi_argument_type_count),
.abi_argument_types = abi_argument_types.ptr,
.abi_return_type = abi_return_type,
.argument_type_abis = argument_type_abis.ptr,
.return_type_abi = return_type_abi,
},
},
});
if (converter.consume_character_if_match(';')) {
const value = module.values.add();
value.* = .{
.llvm = llvm_handle.to_value(),
.type = function_type,
.bb = .external_function,
};
llvm_handle.set_calling_convention(calling_convention.to_llvm());
const has_semicolon = converter.consume_character_if_match(';');
global.* = .{
.value = value,
.name = global_name,
};
} else {
const entry_block = module.llvm.context.create_basic_block("entry", llvm_handle);
module.llvm.builder.position_at_end(entry_block);
const value = module.values.add();
value.* = .{
.llvm = llvm_handle.to_value(),
.type = function_type,
.bb = switch (has_semicolon) {
true => .external_function,
false => .{
.function = .{
.current_basic_block = blk: {
const entry_block = module.llvm.context.create_basic_block("entry", llvm_handle);
module.llvm.builder.position_at_end(entry_block);
break :blk entry_block;
},
.current_scope = current_scope,
.attributes = function_attributes,
},
},
},
};
const global = module.globals.add();
global.* = .{
.value = value,
.name = global_name,
};
llvm_emit_function_site_attributes(module, value);
if (!has_semicolon) {
var llvm_argument_buffer: [argument_buffer.len]*llvm.Argument = undefined;
llvm_handle.get_arguments(&llvm_argument_buffer);
const llvm_arguments = llvm_argument_buffer[0..semantic_argument_count];
const value = module.values.add();
value.* = .{
.llvm = llvm_handle.to_value(),
.type = function_type,
.bb = .{
.function = .{
.current_basic_block = entry_block,
.current_scope = current_scope,
},
},
};
global.* = .{
.value = value,
.name = global_name,
};
module.current_function = global;
defer module.current_function = null;

View File

@ -7,6 +7,8 @@ pub extern fn llvm_context_create_module(context: *llvm.Context, name: llvm.Stri
pub extern fn LLVMContextCreate() *llvm.Context;
pub extern fn LLVMCreateBuilderInContext(context: *llvm.Context) *llvm.Builder;
pub extern fn LLVMIsACallInst(value: *llvm.Value) ?*llvm.Instruction.Call;
// Module
pub extern fn llvm_module_create_global_variable(module: *llvm.Module, global_type: *llvm.Type, is_constant: bool, linkage: llvm.LinkageType, initial_value: *llvm.Constant, name: llvm.String, before: ?*llvm.GlobalVariable, thread_local_mode: llvm.ThreadLocalMode, address_space: c_uint, externally_initialized: bool) *llvm.GlobalVariable;
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;
@ -17,8 +19,8 @@ pub extern fn LLVMGetBasicBlockTerminator(basic_block: *llvm.BasicBlock) ?*llvm.
pub extern fn LLVMSetFunctionCallConv(function: *llvm.Function, calling_convention: llvm.CallingConvention) void;
pub extern fn LLVMGetFunctionCallConv(function: *llvm.Function) llvm.CallingConvention;
pub extern fn LLVMSetInstructionCallConv(instruction: *llvm.Instruction, calling_convention: llvm.CallingConvention) void;
pub extern fn LLVMGetInstructionCallConv(instruction: *llvm.Instruction) llvm.CallingConvention;
pub extern fn LLVMSetInstructionCallConv(instruction: *llvm.Instruction.Call, calling_convention: llvm.CallingConvention) void;
pub extern fn LLVMGetInstructionCallConv(instruction: *llvm.Instruction.Call) llvm.CallingConvention;
pub extern fn LLVMGetParams(function: *llvm.Function, argument_buffer: [*]*llvm.Argument) void;
@ -70,6 +72,17 @@ pub extern fn LLVMLookupIntrinsicID(name_pointer: [*]const u8, name_length: usiz
pub extern fn LLVMGetIntrinsicDeclaration(module: *llvm.Module, intrinsic_id: llvm.Intrinsic.Id, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: usize) *llvm.Value;
pub extern fn LLVMIntrinsicGetType(context: *llvm.Context, intrinsic_id: llvm.Intrinsic.Id, parameter_type_pointer: [*]const *llvm.Type, parameter_type_count: usize) *llvm.Type.Function;
// Attributes
pub extern fn LLVMGetEnumAttributeKindForName(name_pointer: [*]const u8, name_length: usize) llvm.Attribute.Kind;
pub extern fn LLVMCreateEnumAttribute(context: *llvm.Context, kind: llvm.Attribute.Kind, value: u64) *llvm.Attribute;
pub extern fn LLVMCreateTypeAttribute(context: *llvm.Context, kind: llvm.Attribute.Kind, ty: *llvm.Type) *llvm.Attribute;
pub extern fn LLVMCreateConstantRangeAttribute(context: *llvm.Context, kind: llvm.Attribute.Kind, bit_count: c_uint, lower_words: [*]const u64, upper_words: [*]const u64) *llvm.Attribute;
pub extern fn LLVMCreateStringAttribute(context: *llvm.Context, key_pointer: [*]const u8, key_length: c_uint, value_pointer: [*]const u8, value_length: usize) *llvm.Attribute;
pub extern fn LLVMAddAttributeAtIndex(function: *llvm.Function, attribute_index: llvm.Attribute.Index, attribute: *llvm.Attribute) void;
pub extern fn LLVMAddCallSiteAttribute(call: *llvm.Instruction.Call, attribute_index: llvm.Attribute.Index, attribute: *llvm.Attribute) void;
// TYPES
// Types: integers
pub extern fn LLVMVoidTypeInContext(context: *llvm.Context) *llvm.Type;