parent
3be4b6434c
commit
8c9016b6d1
|
@ -667,3 +667,11 @@ Returns whether a given type is a float.
|
|||
### @canImplicitCast(comptime T: type, value) -> bool
|
||||
|
||||
Returns whether a value can be implicitly casted to a given type.
|
||||
|
||||
### @setGlobalAlign(global_variable_name, byte_count: usize) -> bool
|
||||
|
||||
Sets the alignment property of a global variable.
|
||||
|
||||
### @setGlobalSection(global_variable_name, section_name: []u8) -> bool
|
||||
|
||||
Puts the global variable in the specified section.
|
||||
|
|
|
@ -1107,6 +1107,8 @@ enum BuiltinFnId {
|
|||
BuiltinFnIdIsInteger,
|
||||
BuiltinFnIdIsFloat,
|
||||
BuiltinFnIdCanImplicitCast,
|
||||
BuiltinFnIdSetGlobalAlign,
|
||||
BuiltinFnIdSetGlobalSection,
|
||||
};
|
||||
|
||||
struct BuiltinFnEntry {
|
||||
|
@ -1304,6 +1306,10 @@ struct VariableTableEntry {
|
|||
size_t mem_slot_index;
|
||||
size_t ref_count;
|
||||
VarLinkage linkage;
|
||||
AstNode *set_global_align_node;
|
||||
uint64_t alignment;
|
||||
AstNode *set_global_section_node;
|
||||
Buf *section_name;
|
||||
};
|
||||
|
||||
struct ErrorTableEntry {
|
||||
|
@ -1540,6 +1546,8 @@ enum IrInstructionId {
|
|||
IrInstructionIdTestType,
|
||||
IrInstructionIdTypeName,
|
||||
IrInstructionIdCanImplicitCast,
|
||||
IrInstructionIdSetGlobalAlign,
|
||||
IrInstructionIdSetGlobalSection,
|
||||
};
|
||||
|
||||
struct IrInstruction {
|
||||
|
@ -2243,6 +2251,20 @@ struct IrInstructionCanImplicitCast {
|
|||
IrInstruction *target_value;
|
||||
};
|
||||
|
||||
struct IrInstructionSetGlobalAlign {
|
||||
IrInstruction base;
|
||||
|
||||
VariableTableEntry *var;
|
||||
IrInstruction *value;
|
||||
};
|
||||
|
||||
struct IrInstructionSetGlobalSection {
|
||||
IrInstruction base;
|
||||
|
||||
VariableTableEntry *var;
|
||||
IrInstruction *value;
|
||||
};
|
||||
|
||||
enum LValPurpose {
|
||||
LValPurposeNone,
|
||||
LValPurposeAssign,
|
||||
|
|
|
@ -231,7 +231,7 @@ void codegen_set_linker_script(CodeGen *g, const char *linker_script) {
|
|||
|
||||
|
||||
static void render_const_val(CodeGen *g, ConstExprValue *const_val);
|
||||
static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, bool is_export);
|
||||
static void render_const_val_global(CodeGen *g, ConstExprValue *const_val);
|
||||
|
||||
static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
|
||||
if (fn_table_entry->llvm_value)
|
||||
|
@ -686,7 +686,7 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
|
|||
// we might have to do some pointer casting here due to the way union
|
||||
// values are rendered with a type other than the one we expect
|
||||
if (handle_is_ptr(instruction->value.type)) {
|
||||
render_const_val_global(g, &instruction->value, false);
|
||||
render_const_val_global(g, &instruction->value);
|
||||
TypeTableEntry *ptr_type = get_pointer_to_type(g, instruction->value.type, true);
|
||||
instruction->llvm_value = LLVMBuildBitCast(g->builder, instruction->value.llvm_global, ptr_type->type_ref, "");
|
||||
} else if (instruction->value.type->id == TypeTableEntryIdPointer) {
|
||||
|
@ -2282,6 +2282,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
|||
case IrInstructionIdTestType:
|
||||
case IrInstructionIdTypeName:
|
||||
case IrInstructionIdCanImplicitCast:
|
||||
case IrInstructionIdSetGlobalAlign:
|
||||
case IrInstructionIdSetGlobalSection:
|
||||
zig_unreachable();
|
||||
case IrInstructionIdReturn:
|
||||
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
|
||||
|
@ -2420,7 +2422,7 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *ar
|
|||
base_ptr = gen_const_ptr_array_recursive(g, parent_array, parent_array_index);
|
||||
} else {
|
||||
render_const_val(g, array_const_val);
|
||||
render_const_val_global(g, array_const_val, false);
|
||||
render_const_val_global(g, array_const_val);
|
||||
base_ptr = array_const_val->llvm_global;
|
||||
}
|
||||
TypeTableEntry *usize = g->builtin_types.entry_usize;
|
||||
|
@ -2568,16 +2570,16 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
|
|||
return fn_llvm_value(g, const_val->data.x_fn);
|
||||
case TypeTableEntryIdPointer:
|
||||
{
|
||||
render_const_val_global(g, const_val, false);
|
||||
render_const_val_global(g, const_val);
|
||||
size_t index = const_val->data.x_ptr.index;
|
||||
ConstExprValue *base_ptr = const_val->data.x_ptr.base_ptr;
|
||||
if (base_ptr) {
|
||||
if (index == SIZE_MAX) {
|
||||
render_const_val(g, base_ptr);
|
||||
render_const_val_global(g, base_ptr, false);
|
||||
render_const_val_global(g, base_ptr);
|
||||
ConstExprValue *other_val = base_ptr;
|
||||
const_val->llvm_value = LLVMConstBitCast(other_val->llvm_global, const_val->type->type_ref);
|
||||
render_const_val_global(g, const_val, false);
|
||||
render_const_val_global(g, const_val);
|
||||
return const_val->llvm_value;
|
||||
} else {
|
||||
ConstExprValue *array_const_val = base_ptr;
|
||||
|
@ -2587,19 +2589,19 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
|
|||
TypeTableEntry *usize = g->builtin_types.entry_usize;
|
||||
const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
|
||||
const_val->type->type_ref);
|
||||
render_const_val_global(g, const_val, false);
|
||||
render_const_val_global(g, const_val);
|
||||
return const_val->llvm_value;
|
||||
}
|
||||
LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, index);
|
||||
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
|
||||
const_val->llvm_value = ptr_val;
|
||||
render_const_val_global(g, const_val, false);
|
||||
render_const_val_global(g, const_val);
|
||||
return ptr_val;
|
||||
}
|
||||
} else {
|
||||
TypeTableEntry *usize = g->builtin_types.entry_usize;
|
||||
const_val->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, index, false), const_val->type->type_ref);
|
||||
render_const_val_global(g, const_val, false);
|
||||
render_const_val_global(g, const_val);
|
||||
return const_val->llvm_value;
|
||||
}
|
||||
}
|
||||
|
@ -2654,11 +2656,11 @@ static void render_const_val(CodeGen *g, ConstExprValue *const_val) {
|
|||
LLVMSetInitializer(const_val->llvm_global, const_val->llvm_value);
|
||||
}
|
||||
|
||||
static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, bool is_export) {
|
||||
static void render_const_val_global(CodeGen *g, ConstExprValue *const_val) {
|
||||
if (!const_val->llvm_global) {
|
||||
LLVMTypeRef type_ref = const_val->llvm_value ? LLVMTypeOf(const_val->llvm_value) : const_val->type->type_ref;
|
||||
LLVMValueRef global_value = LLVMAddGlobal(g->module, type_ref, "");
|
||||
LLVMSetLinkage(global_value, is_export ? LLVMExternalLinkage : LLVMInternalLinkage);
|
||||
LLVMSetLinkage(global_value, LLVMInternalLinkage);
|
||||
LLVMSetGlobalConstant(global_value, true);
|
||||
LLVMSetUnnamedAddr(global_value, true);
|
||||
|
||||
|
@ -2840,10 +2842,20 @@ static void do_code_gen(CodeGen *g) {
|
|||
|
||||
LLVMSetLinkage(global_value, LLVMExternalLinkage);
|
||||
} else {
|
||||
bool is_export = (var->linkage == VarLinkageExport);
|
||||
render_const_val(g, &var->value);
|
||||
render_const_val_global(g, &var->value, is_export);
|
||||
render_const_val_global(g, &var->value);
|
||||
global_value = var->value.llvm_global;
|
||||
|
||||
if (var->linkage == VarLinkageExport) {
|
||||
LLVMSetLinkage(global_value, LLVMExternalLinkage);
|
||||
}
|
||||
if (var->section_name) {
|
||||
LLVMSetSection(global_value, buf_ptr(var->section_name));
|
||||
}
|
||||
if (var->alignment) {
|
||||
LLVMSetAlignment(global_value, var->alignment);
|
||||
}
|
||||
|
||||
// TODO debug info for function pointers
|
||||
if (var->gen_is_const && var->value.type->id != TypeTableEntryIdFn) {
|
||||
gen_global_var(g, var, var->value.llvm_value, var->value.type);
|
||||
|
@ -3672,6 +3684,8 @@ static void define_builtin_fns(CodeGen *g) {
|
|||
create_builtin_fn(g, BuiltinFnIdSetFnVisible, "setFnVisible", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdAlloca, "alloca", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetGlobalAlign, "setGlobalAlign", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2);
|
||||
}
|
||||
|
||||
static void init(CodeGen *g, Buf *source_path) {
|
||||
|
|
139
src/ir.cpp
139
src/ir.cpp
|
@ -507,6 +507,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCanImplicitCast
|
|||
return IrInstructionIdCanImplicitCast;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetGlobalAlign *) {
|
||||
return IrInstructionIdSetGlobalAlign;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetGlobalSection *) {
|
||||
return IrInstructionIdSetGlobalSection;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
||||
T *special_instruction = allocate<T>(1);
|
||||
|
@ -2052,6 +2060,32 @@ static IrInstruction *ir_build_can_implicit_cast(IrBuilder *irb, Scope *scope, A
|
|||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_set_global_align(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
VariableTableEntry *var, IrInstruction *value)
|
||||
{
|
||||
IrInstructionSetGlobalAlign *instruction = ir_build_instruction<IrInstructionSetGlobalAlign>(
|
||||
irb, scope, source_node);
|
||||
instruction->var = var;
|
||||
instruction->value = value;
|
||||
|
||||
ir_ref_instruction(value, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_set_global_section(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
VariableTableEntry *var, IrInstruction *value)
|
||||
{
|
||||
IrInstructionSetGlobalSection *instruction = ir_build_instruction<IrInstructionSetGlobalSection>(
|
||||
irb, scope, source_node);
|
||||
instruction->var = var;
|
||||
instruction->value = value;
|
||||
|
||||
ir_ref_instruction(value, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -2678,6 +2712,20 @@ static IrInstruction *ir_instruction_canimplicitcast_get_dep(IrInstructionCanImp
|
|||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_setglobalalign_get_dep(IrInstructionSetGlobalAlign *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->value;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_setglobalsection_get_dep(IrInstructionSetGlobalSection *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->value;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
|
||||
switch (instruction->id) {
|
||||
case IrInstructionIdInvalid:
|
||||
|
@ -2856,6 +2904,10 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
|
|||
return ir_instruction_typename_get_dep((IrInstructionTypeName *) instruction, index);
|
||||
case IrInstructionIdCanImplicitCast:
|
||||
return ir_instruction_canimplicitcast_get_dep((IrInstructionCanImplicitCast *) instruction, index);
|
||||
case IrInstructionIdSetGlobalAlign:
|
||||
return ir_instruction_setglobalalign_get_dep((IrInstructionSetGlobalAlign *) instruction, index);
|
||||
case IrInstructionIdSetGlobalSection:
|
||||
return ir_instruction_setglobalsection_get_dep((IrInstructionSetGlobalSection *) instruction, index);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
@ -4113,6 +4165,40 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
|||
|
||||
return ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value);
|
||||
}
|
||||
case BuiltinFnIdSetGlobalAlign:
|
||||
case BuiltinFnIdSetGlobalSection:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
if (arg0_node->type != NodeTypeSymbol) {
|
||||
add_node_error(irb->codegen, arg0_node, buf_sprintf("expected identifier"));
|
||||
return irb->codegen->invalid_instruction;
|
||||
}
|
||||
Buf *variable_name = arg0_node->data.symbol_expr.symbol;
|
||||
Tld *tld = find_decl(scope, variable_name);
|
||||
if (!tld) {
|
||||
add_node_error(irb->codegen, node, buf_sprintf("use of undeclared identifier '%s'",
|
||||
buf_ptr(variable_name)));
|
||||
return irb->codegen->invalid_instruction;
|
||||
}
|
||||
if (tld->id != TldIdVar) {
|
||||
add_node_error(irb->codegen, node, buf_sprintf("'%s' is not a global variable",
|
||||
buf_ptr(variable_name)));
|
||||
return irb->codegen->invalid_instruction;
|
||||
}
|
||||
TldVar *tld_var = (TldVar *)tld;
|
||||
VariableTableEntry *var = tld_var->var;
|
||||
|
||||
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
||||
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
||||
if (arg1_value == irb->codegen->invalid_instruction)
|
||||
return arg1_value;
|
||||
|
||||
if (builtin_fn->id == BuiltinFnIdSetGlobalAlign) {
|
||||
return ir_build_set_global_align(irb, scope, node, var, arg1_value);
|
||||
} else {
|
||||
return ir_build_set_global_section(irb, scope, node, var, arg1_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
@ -9350,6 +9436,53 @@ static TypeTableEntry *ir_analyze_instruction_set_fn_visible(IrAnalyze *ira,
|
|||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_set_global_align(IrAnalyze *ira,
|
||||
IrInstructionSetGlobalAlign *instruction)
|
||||
{
|
||||
VariableTableEntry *var = instruction->var;
|
||||
IrInstruction *align_value = instruction->value->other;
|
||||
|
||||
uint64_t scalar_align;
|
||||
if (!ir_resolve_usize(ira, align_value, &scalar_align))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
AstNode *source_node = instruction->base.source_node;
|
||||
if (var->set_global_align_node) {
|
||||
ErrorMsg *msg = ir_add_error_node(ira, source_node,
|
||||
buf_sprintf("alignment set twice"));
|
||||
add_error_note(ira->codegen, msg, var->set_global_align_node, buf_sprintf("first set here"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
var->set_global_align_node = source_node;
|
||||
var->alignment = scalar_align;
|
||||
|
||||
ir_build_const_from(ira, &instruction->base, false);
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_set_global_section(IrAnalyze *ira,
|
||||
IrInstructionSetGlobalSection *instruction)
|
||||
{
|
||||
VariableTableEntry *var = instruction->var;
|
||||
IrInstruction *section_value = instruction->value->other;
|
||||
|
||||
Buf *section_name = ir_resolve_str(ira, section_value);
|
||||
if (!section_name)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
AstNode *source_node = instruction->base.source_node;
|
||||
if (var->set_global_section_node) {
|
||||
ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("section set twice"));
|
||||
add_error_note(ira->codegen, msg, var->set_global_section_node, buf_sprintf("first set here"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
var->set_global_section_node = source_node;
|
||||
var->section_name = section_name;
|
||||
|
||||
ir_build_const_from(ira, &instruction->base, false);
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,
|
||||
IrInstructionSetDebugSafety *set_debug_safety_instruction)
|
||||
{
|
||||
|
@ -11779,6 +11912,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
|||
return ir_analyze_instruction_set_fn_test(ira, (IrInstructionSetFnTest *)instruction);
|
||||
case IrInstructionIdSetFnVisible:
|
||||
return ir_analyze_instruction_set_fn_visible(ira, (IrInstructionSetFnVisible *)instruction);
|
||||
case IrInstructionIdSetGlobalAlign:
|
||||
return ir_analyze_instruction_set_global_align(ira, (IrInstructionSetGlobalAlign *)instruction);
|
||||
case IrInstructionIdSetGlobalSection:
|
||||
return ir_analyze_instruction_set_global_section(ira, (IrInstructionSetGlobalSection *)instruction);
|
||||
case IrInstructionIdSetDebugSafety:
|
||||
return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction);
|
||||
case IrInstructionIdSliceType:
|
||||
|
@ -11993,6 +12130,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
|||
case IrInstructionIdBreakpoint:
|
||||
case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free
|
||||
case IrInstructionIdCheckSwitchProngs:
|
||||
case IrInstructionIdSetGlobalAlign:
|
||||
case IrInstructionIdSetGlobalSection:
|
||||
return true;
|
||||
case IrInstructionIdPhi:
|
||||
case IrInstructionIdUnOp:
|
||||
|
|
|
@ -233,11 +233,15 @@ static void ir_print_phi(IrPrint *irp, IrInstructionPhi *phi_instruction) {
|
|||
static void ir_print_container_init_list(IrPrint *irp, IrInstructionContainerInitList *instruction) {
|
||||
ir_print_other_instruction(irp, instruction->container_type);
|
||||
fprintf(irp->f, "{");
|
||||
for (size_t i = 0; i < instruction->item_count; i += 1) {
|
||||
IrInstruction *item = instruction->items[i];
|
||||
if (i != 0)
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, item);
|
||||
if (instruction->item_count > 50) {
|
||||
fprintf(irp->f, "...(%zu items)...", instruction->item_count);
|
||||
} else {
|
||||
for (size_t i = 0; i < instruction->item_count; i += 1) {
|
||||
IrInstruction *item = instruction->items[i];
|
||||
if (i != 0)
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, item);
|
||||
}
|
||||
}
|
||||
fprintf(irp->f, "}");
|
||||
}
|
||||
|
@ -827,6 +831,18 @@ static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCas
|
|||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_set_global_align(IrPrint *irp, IrInstructionSetGlobalAlign *instruction) {
|
||||
fprintf(irp->f, "@setGlobalAlign(%s,", buf_ptr(&instruction->var->name));
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_set_global_section(IrPrint *irp, IrInstructionSetGlobalSection *instruction) {
|
||||
fprintf(irp->f, "@setGlobalSection(%s,", buf_ptr(&instruction->var->name));
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
ir_print_prefix(irp, instruction);
|
||||
switch (instruction->id) {
|
||||
|
@ -1093,6 +1109,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
|||
case IrInstructionIdCanImplicitCast:
|
||||
ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction);
|
||||
break;
|
||||
case IrInstructionIdSetGlobalAlign:
|
||||
ir_print_set_global_align(irp, (IrInstructionSetGlobalAlign *)instruction);
|
||||
break;
|
||||
case IrInstructionIdSetGlobalSection:
|
||||
ir_print_set_global_section(irp, (IrInstructionSetGlobalSection *)instruction);
|
||||
break;
|
||||
}
|
||||
fprintf(irp->f, "\n");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue