mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 05:20:34 +00:00
IR: implement alloca builtin
This commit is contained in:
parent
a963fba246
commit
fb21570630
@ -370,6 +370,19 @@ TODO
|
||||
Built-in functions are prefixed with `@`. Remember that the `inline` keyword on
|
||||
a parameter means that the parameter must be known at compile time.
|
||||
|
||||
### @alloca(inline T: type, count: usize) -> []T
|
||||
|
||||
Allocates memory in the stack frame of the caller. This temporary space is
|
||||
automatically freed when the function that called alloca returns to its caller,
|
||||
just like other stack variables.
|
||||
|
||||
When using this function to allocate memory, you should know the upper bound
|
||||
of `count`. Consider putting a constant array on the stack with the upper bound
|
||||
instead of using alloca. If you do use alloca it is to save a few bytes off
|
||||
the memory size given that you didn't actually hit your upper bound.
|
||||
|
||||
The allocated memory contents are undefined.
|
||||
|
||||
### @typeof(expression) -> type
|
||||
|
||||
This function returns a compile-time constant, which is the type of the
|
||||
|
||||
@ -1044,6 +1044,7 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdSetFnTest,
|
||||
BuiltinFnIdSetFnVisible,
|
||||
BuiltinFnIdSetDebugSafety,
|
||||
BuiltinFnIdAlloca,
|
||||
};
|
||||
|
||||
struct BuiltinFnEntry {
|
||||
@ -1413,6 +1414,7 @@ enum IrInstructionId {
|
||||
IrInstructionIdTruncate,
|
||||
IrInstructionIdIntType,
|
||||
IrInstructionIdBoolNot,
|
||||
IrInstructionIdAlloca,
|
||||
};
|
||||
|
||||
struct IrInstruction {
|
||||
@ -1914,6 +1916,14 @@ struct IrInstructionBoolNot {
|
||||
IrInstruction *value;
|
||||
};
|
||||
|
||||
struct IrInstructionAlloca {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *type_value;
|
||||
IrInstruction *count;
|
||||
LLVMValueRef tmp_ptr;
|
||||
};
|
||||
|
||||
enum LValPurpose {
|
||||
LValPurposeNone,
|
||||
LValPurposeAssign,
|
||||
|
||||
@ -1881,13 +1881,31 @@ static LLVMValueRef ir_render_div_exact(CodeGen *g, IrExecutable *executable, Ir
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrInstructionTruncate *instruction) {
|
||||
assert(instruction->dest_type->type_entry->id == TypeTableEntryIdMetaType);
|
||||
TypeTableEntry *dest_type = get_underlying_type(instruction->dest_type->static_value.data.x_type);
|
||||
assert(dest_type->id == TypeTableEntryIdInt);
|
||||
TypeTableEntry *dest_type = get_underlying_type(instruction->base.type_entry);
|
||||
LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
|
||||
return LLVMBuildTrunc(g->builder, target_val, dest_type->type_ref, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_alloca(CodeGen *g, IrExecutable *executable, IrInstructionAlloca *instruction) {
|
||||
TypeTableEntry *slice_type = get_underlying_type(instruction->base.type_entry);
|
||||
TypeTableEntry *ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry;
|
||||
TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
|
||||
LLVMValueRef size_val = ir_llvm_value(g, instruction->count);
|
||||
LLVMValueRef ptr_val = LLVMBuildArrayAlloca(g->builder, child_type->type_ref, size_val, "");
|
||||
|
||||
// TODO in debug mode, initialize all the bytes to 0xaa
|
||||
|
||||
// store the freshly allocated pointer in the slice
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, slice_ptr_index, "");
|
||||
LLVMBuildStore(g->builder, ptr_val, ptr_field_ptr);
|
||||
|
||||
// store the size in the len field
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, slice_len_index, "");
|
||||
LLVMBuildStore(g->builder, size_val, len_field_ptr);
|
||||
|
||||
return instruction->tmp_ptr;
|
||||
}
|
||||
|
||||
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
|
||||
AstNode *source_node = instruction->source_node;
|
||||
Scope *scope = instruction->scope;
|
||||
@ -1988,6 +2006,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_truncate(g, executable, (IrInstructionTruncate *)instruction);
|
||||
case IrInstructionIdBoolNot:
|
||||
return ir_render_bool_not(g, executable, (IrInstructionBoolNot *)instruction);
|
||||
case IrInstructionIdAlloca:
|
||||
return ir_render_alloca(g, executable, (IrInstructionAlloca *)instruction);
|
||||
case IrInstructionIdSwitchVar:
|
||||
case IrInstructionIdContainerInitList:
|
||||
case IrInstructionIdStructInit:
|
||||
@ -2567,6 +2587,9 @@ static void do_code_gen(CodeGen *g) {
|
||||
} else if (instruction->id == IrInstructionIdCall) {
|
||||
IrInstructionCall *call_instruction = (IrInstructionCall *)instruction;
|
||||
slot = &call_instruction->tmp_ptr;
|
||||
} else if (instruction->id == IrInstructionIdAlloca) {
|
||||
IrInstructionAlloca *alloca_instruction = (IrInstructionAlloca *)instruction;
|
||||
slot = &alloca_instruction->tmp_ptr;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -3191,6 +3214,7 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdSetFnTest, "setFnTest", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdSetFnVisible, "setFnVisible", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdAlloca, "alloca", 2);
|
||||
}
|
||||
|
||||
static void init(CodeGen *g, Buf *source_path) {
|
||||
|
||||
217
src/ir.cpp
217
src/ir.cpp
@ -371,6 +371,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) {
|
||||
return IrInstructionIdBoolNot;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlloca *) {
|
||||
return IrInstructionIdAlloca;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
|
||||
T *special_instruction = allocate<T>(1);
|
||||
@ -1489,6 +1493,27 @@ static IrInstruction *ir_build_bool_not_from(IrBuilder *irb, IrInstruction *old_
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_alloca(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *type_value, IrInstruction *count)
|
||||
{
|
||||
IrInstructionAlloca *instruction = ir_build_instruction<IrInstructionAlloca>(irb, scope, source_node);
|
||||
instruction->type_value = type_value;
|
||||
instruction->count = count;
|
||||
|
||||
ir_ref_instruction(type_value);
|
||||
ir_ref_instruction(count);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_alloca_from(IrBuilder *irb, IrInstruction *old_instruction,
|
||||
IrInstruction *type_value, IrInstruction *count)
|
||||
{
|
||||
IrInstruction *new_instruction = ir_build_alloca(irb, old_instruction->scope, old_instruction->source_node, type_value, count);
|
||||
ir_link_new_instruction(new_instruction, old_instruction);
|
||||
return new_instruction;
|
||||
}
|
||||
|
||||
static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope,
|
||||
bool gen_error_defers, bool gen_maybe_defers)
|
||||
{
|
||||
@ -2310,6 +2335,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
||||
|
||||
return ir_build_int_type(irb, scope, node, arg0_value, arg1_value);
|
||||
}
|
||||
case BuiltinFnIdAlloca:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
||||
if (arg0_value == irb->codegen->invalid_instruction)
|
||||
return arg0_value;
|
||||
|
||||
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;
|
||||
|
||||
return ir_build_alloca(irb, scope, node, arg0_value, arg1_value);
|
||||
}
|
||||
case BuiltinFnIdMemcpy:
|
||||
case BuiltinFnIdMemset:
|
||||
case BuiltinFnIdAlignof:
|
||||
@ -7876,6 +7915,69 @@ static TypeTableEntry *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstruc
|
||||
return bool_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAlloca *instruction) {
|
||||
IrInstruction *type_value = instruction->type_value->other;
|
||||
if (type_value->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *count_value = instruction->count->other;
|
||||
if (count_value->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
TypeTableEntry *child_type = ir_resolve_type(ira, type_value);
|
||||
TypeTableEntry *canon_type = get_underlying_type(child_type);
|
||||
|
||||
if (count_value->static_value.special == ConstValSpecialStatic) {
|
||||
// this should be the same as an array declaration
|
||||
|
||||
uint64_t count;
|
||||
if (!ir_resolve_usize(ira, count_value, &count))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
zig_panic("TODO alloca with compile time known count");
|
||||
}
|
||||
|
||||
switch (canon_type->id) {
|
||||
case TypeTableEntryIdInvalid:
|
||||
case TypeTableEntryIdTypeDecl:
|
||||
zig_unreachable();
|
||||
case TypeTableEntryIdBool:
|
||||
case TypeTableEntryIdVoid:
|
||||
case TypeTableEntryIdInt:
|
||||
case TypeTableEntryIdFloat:
|
||||
case TypeTableEntryIdPointer:
|
||||
case TypeTableEntryIdArray:
|
||||
case TypeTableEntryIdStruct:
|
||||
case TypeTableEntryIdMaybe:
|
||||
case TypeTableEntryIdErrorUnion:
|
||||
case TypeTableEntryIdPureError:
|
||||
case TypeTableEntryIdEnum:
|
||||
case TypeTableEntryIdUnion:
|
||||
case TypeTableEntryIdFn:
|
||||
{
|
||||
TypeTableEntry *slice_type = get_slice_type(ira->codegen, child_type, false);
|
||||
IrInstruction *new_instruction = ir_build_alloca_from(&ira->new_irb, &instruction->base, type_value, count_value);
|
||||
ir_add_alloca(ira, new_instruction, slice_type);
|
||||
return slice_type;
|
||||
}
|
||||
case TypeTableEntryIdVar:
|
||||
case TypeTableEntryIdMetaType:
|
||||
case TypeTableEntryIdUnreachable:
|
||||
case TypeTableEntryIdNumLitFloat:
|
||||
case TypeTableEntryIdNumLitInt:
|
||||
case TypeTableEntryIdUndefLit:
|
||||
case TypeTableEntryIdNullLit:
|
||||
case TypeTableEntryIdNamespace:
|
||||
case TypeTableEntryIdBlock:
|
||||
case TypeTableEntryIdBoundFn:
|
||||
ir_add_error(ira, type_value,
|
||||
buf_sprintf("invalid alloca type '%s'", buf_ptr(&child_type->name)));
|
||||
// TODO if this is a typedecl, add error note showing the declaration of the type decl
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
|
||||
switch (instruction->id) {
|
||||
case IrInstructionIdInvalid:
|
||||
@ -7990,6 +8092,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||
return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction);
|
||||
case IrInstructionIdBoolNot:
|
||||
return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction);
|
||||
case IrInstructionIdAlloca:
|
||||
return ir_analyze_instruction_alloca(ira, (IrInstructionAlloca *)instruction);
|
||||
case IrInstructionIdCast:
|
||||
case IrInstructionIdStructFieldPtr:
|
||||
case IrInstructionIdEnumFieldPtr:
|
||||
@ -8131,6 +8235,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
case IrInstructionIdTruncate:
|
||||
case IrInstructionIdIntType:
|
||||
case IrInstructionIdBoolNot:
|
||||
case IrInstructionIdAlloca:
|
||||
return false;
|
||||
case IrInstructionIdAsm:
|
||||
{
|
||||
@ -8998,115 +9103,3 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
// }
|
||||
// zig_unreachable();
|
||||
//}
|
||||
//
|
||||
//static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl,
|
||||
// bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type, bool var_is_ptr)
|
||||
//{
|
||||
// VariableTableEntry *variable = var_decl->variable;
|
||||
//
|
||||
// assert(variable);
|
||||
//
|
||||
// if (var_decl->expr) {
|
||||
// *init_value = gen_expr(g, var_decl->expr);
|
||||
// *expr_type = get_expr_type(var_decl->expr);
|
||||
// }
|
||||
// if (!type_has_bits(variable->type)) {
|
||||
// return nullptr;
|
||||
// }
|
||||
//
|
||||
// bool have_init_expr = false;
|
||||
// bool want_zeroes = false;
|
||||
// if (var_decl->expr) {
|
||||
// ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val;
|
||||
// if (!const_val->ok || const_val->special == ConstValSpecialOther) {
|
||||
// have_init_expr = true;
|
||||
// }
|
||||
// if (const_val->ok && const_val->special == ConstValSpecialZeroes) {
|
||||
// want_zeroes = true;
|
||||
// }
|
||||
// }
|
||||
// if (have_init_expr) {
|
||||
// TypeTableEntry *expr_type = get_expr_type(var_decl->expr);
|
||||
// LLVMValueRef value;
|
||||
// if (unwrap_maybe) {
|
||||
// assert(var_decl->expr);
|
||||
// assert(expr_type->id == TypeTableEntryIdMaybe);
|
||||
// value = gen_unwrap_maybe(g, var_decl->expr, *init_value);
|
||||
// expr_type = expr_type->data.maybe.child_type;
|
||||
// } else {
|
||||
// value = *init_value;
|
||||
// }
|
||||
// gen_assign_raw(g, var_decl->expr, BinOpTypeAssign, variable->value_ref,
|
||||
// value, variable->type, expr_type);
|
||||
// } else {
|
||||
// bool ignore_uninit = false;
|
||||
// // handle runtime stack allocation
|
||||
// if (var_decl->type) {
|
||||
// TypeTableEntry *var_type = get_type_for_type_node(var_decl->type);
|
||||
// if (var_type->id == TypeTableEntryIdStruct &&
|
||||
// var_type->data.structure.is_slice)
|
||||
// {
|
||||
// assert(var_decl->type->type == NodeTypeArrayType);
|
||||
// AstNode *size_node = var_decl->type->data.array_type.size;
|
||||
// if (size_node) {
|
||||
// ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
|
||||
// if (!const_val->ok) {
|
||||
// TypeTableEntry *ptr_type = var_type->data.structure.fields[0].type_entry;
|
||||
// assert(ptr_type->id == TypeTableEntryIdPointer);
|
||||
// TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
|
||||
//
|
||||
// LLVMValueRef size_val = gen_expr(g, size_node);
|
||||
//
|
||||
// set_debug_source_node(g, source_node);
|
||||
// LLVMValueRef ptr_val = LLVMBuildArrayAlloca(g->builder, child_type->type_ref,
|
||||
// size_val, "");
|
||||
//
|
||||
// size_t ptr_index = var_type->data.structure.fields[0].gen_index;
|
||||
// assert(ptr_index != SIZE_MAX);
|
||||
// size_t len_index = var_type->data.structure.fields[1].gen_index;
|
||||
// assert(len_index != SIZE_MAX);
|
||||
//
|
||||
// // store the freshly allocated pointer in the unknown size array struct
|
||||
// LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder,
|
||||
// variable->value_ref, ptr_index, "");
|
||||
// LLVMBuildStore(g->builder, ptr_val, ptr_field_ptr);
|
||||
//
|
||||
// // store the size in the len field
|
||||
// LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder,
|
||||
// variable->value_ref, len_index, "");
|
||||
// LLVMBuildStore(g->builder, size_val, len_field_ptr);
|
||||
//
|
||||
// // don't clobber what we just did with debug initialization
|
||||
// ignore_uninit = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// bool want_safe = want_debug_safety(g, source_node);
|
||||
// if (!ignore_uninit && (want_safe || want_zeroes)) {
|
||||
// TypeTableEntry *usize = g->builtin_types.entry_usize;
|
||||
// uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref);
|
||||
// uint64_t align_bytes = get_memcpy_align(g, variable->type);
|
||||
//
|
||||
// // memset uninitialized memory to 0xa
|
||||
// set_debug_source_node(g, source_node);
|
||||
// LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
|
||||
// LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), want_zeroes ? 0x00 : 0xaa, false);
|
||||
// LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, variable->value_ref, ptr_u8, "");
|
||||
// LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false);
|
||||
// LLVMValueRef align_in_bytes = LLVMConstInt(LLVMInt32Type(), align_bytes, false);
|
||||
// LLVMValueRef params[] = {
|
||||
// dest_ptr,
|
||||
// fill_char,
|
||||
// byte_count,
|
||||
// align_in_bytes,
|
||||
// LLVMConstNull(LLVMInt1Type()), // is volatile
|
||||
// };
|
||||
//
|
||||
// LLVMBuildCall(g->builder, g->memset_fn_val, params, 5, "");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// gen_var_debug_decl(g, variable);
|
||||
// return nullptr;
|
||||
//}
|
||||
|
||||
@ -753,6 +753,14 @@ static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction)
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_alloca(IrPrint *irp, IrInstructionAlloca *instruction) {
|
||||
fprintf(irp->f, "@alloca(");
|
||||
ir_print_other_instruction(irp, instruction->type_value);
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, instruction->count);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) {
|
||||
fprintf(irp->f, "@intType(");
|
||||
ir_print_other_instruction(irp, instruction->is_signed);
|
||||
@ -942,6 +950,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdTruncate:
|
||||
ir_print_truncate(irp, (IrInstructionTruncate *)instruction);
|
||||
break;
|
||||
case IrInstructionIdAlloca:
|
||||
ir_print_alloca(irp, (IrInstructionAlloca *)instruction);
|
||||
break;
|
||||
case IrInstructionIdIntType:
|
||||
ir_print_int_type(irp, (IrInstructionIntType *)instruction);
|
||||
break;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user