authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-12-11 19:43:06-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-12-11 19:43:06-05:00
logfb2157063060682a314cb8f2e34efc7989646455
tree2fb7f3c638d82a6816720d1e0cf04f6373fc9191
parenta963fba246819e8b71777db157ad9578086ea16e

IR: implement alloca builtin


5 files changed, 166 insertions(+), 115 deletions(-)

doc/langref.md+13
......@@ -370,6 +370,19 @@ TODO
370370Built-in functions are prefixed with `@`. Remember that the `inline` keyword on
371371a parameter means that the parameter must be known at compile time.
372372
373### @alloca(inline T: type, count: usize) -> []T
374
375Allocates memory in the stack frame of the caller. This temporary space is
376automatically freed when the function that called alloca returns to its caller,
377just like other stack variables.
378
379When using this function to allocate memory, you should know the upper bound
380of `count`. Consider putting a constant array on the stack with the upper bound
381instead of using alloca. If you do use alloca it is to save a few bytes off
382the memory size given that you didn't actually hit your upper bound.
383
384The allocated memory contents are undefined.
385
373386### @typeof(expression) -> type
374387
375388This function returns a compile-time constant, which is the type of the
src/all_types.hpp+10
......@@ -1044,6 +1044,7 @@ enum BuiltinFnId {
10441044 BuiltinFnIdSetFnTest,
10451045 BuiltinFnIdSetFnVisible,
10461046 BuiltinFnIdSetDebugSafety,
1047 BuiltinFnIdAlloca,
10471048};
10481049
10491050struct BuiltinFnEntry {
......@@ -1413,6 +1414,7 @@ enum IrInstructionId {
14131414 IrInstructionIdTruncate,
14141415 IrInstructionIdIntType,
14151416 IrInstructionIdBoolNot,
1417 IrInstructionIdAlloca,
14161418};
14171419
14181420struct IrInstruction {
......@@ -1914,6 +1916,14 @@ struct IrInstructionBoolNot {
19141916 IrInstruction *value;
19151917};
19161918
1919struct IrInstructionAlloca {
1920 IrInstruction base;
1921
1922 IrInstruction *type_value;
1923 IrInstruction *count;
1924 LLVMValueRef tmp_ptr;
1925};
1926
19171927enum LValPurpose {
19181928 LValPurposeNone,
19191929 LValPurposeAssign,
src/codegen.cpp+27-3
......@@ -1881,13 +1881,31 @@ static LLVMValueRef ir_render_div_exact(CodeGen *g, IrExecutable *executable, Ir
18811881}
18821882
18831883static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrInstructionTruncate *instruction) {
1884 assert(instruction->dest_type->type_entry->id == TypeTableEntryIdMetaType);
1885 TypeTableEntry *dest_type = get_underlying_type(instruction->dest_type->static_value.data.x_type);
1886 assert(dest_type->id == TypeTableEntryIdInt);
1884 TypeTableEntry *dest_type = get_underlying_type(instruction->base.type_entry);
18871885 LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
18881886 return LLVMBuildTrunc(g->builder, target_val, dest_type->type_ref, "");
18891887}
18901888
1889static LLVMValueRef ir_render_alloca(CodeGen *g, IrExecutable *executable, IrInstructionAlloca *instruction) {
1890 TypeTableEntry *slice_type = get_underlying_type(instruction->base.type_entry);
1891 TypeTableEntry *ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry;
1892 TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
1893 LLVMValueRef size_val = ir_llvm_value(g, instruction->count);
1894 LLVMValueRef ptr_val = LLVMBuildArrayAlloca(g->builder, child_type->type_ref, size_val, "");
1895
1896 // TODO in debug mode, initialize all the bytes to 0xaa
1897
1898 // store the freshly allocated pointer in the slice
1899 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, slice_ptr_index, "");
1900 LLVMBuildStore(g->builder, ptr_val, ptr_field_ptr);
1901
1902 // store the size in the len field
1903 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, slice_len_index, "");
1904 LLVMBuildStore(g->builder, size_val, len_field_ptr);
1905
1906 return instruction->tmp_ptr;
1907}
1908
18911909static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
18921910 AstNode *source_node = instruction->source_node;
18931911 Scope *scope = instruction->scope;
......@@ -1988,6 +2006,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
19882006 return ir_render_truncate(g, executable, (IrInstructionTruncate *)instruction);
19892007 case IrInstructionIdBoolNot:
19902008 return ir_render_bool_not(g, executable, (IrInstructionBoolNot *)instruction);
2009 case IrInstructionIdAlloca:
2010 return ir_render_alloca(g, executable, (IrInstructionAlloca *)instruction);
19912011 case IrInstructionIdSwitchVar:
19922012 case IrInstructionIdContainerInitList:
19932013 case IrInstructionIdStructInit:
......@@ -2567,6 +2587,9 @@ static void do_code_gen(CodeGen *g) {
25672587 } else if (instruction->id == IrInstructionIdCall) {
25682588 IrInstructionCall *call_instruction = (IrInstructionCall *)instruction;
25692589 slot = &call_instruction->tmp_ptr;
2590 } else if (instruction->id == IrInstructionIdAlloca) {
2591 IrInstructionAlloca *alloca_instruction = (IrInstructionAlloca *)instruction;
2592 slot = &alloca_instruction->tmp_ptr;
25702593 } else {
25712594 zig_unreachable();
25722595 }
......@@ -3191,6 +3214,7 @@ static void define_builtin_fns(CodeGen *g) {
31913214 create_builtin_fn(g, BuiltinFnIdSetFnTest, "setFnTest", 1);
31923215 create_builtin_fn(g, BuiltinFnIdSetFnVisible, "setFnVisible", 2);
31933216 create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);
3217 create_builtin_fn(g, BuiltinFnIdAlloca, "alloca", 2);
31943218}
31953219
31963220static void init(CodeGen *g, Buf *source_path) {
src/ir.cpp+105-112
......@@ -371,6 +371,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) {
371371 return IrInstructionIdBoolNot;
372372}
373373
374static constexpr IrInstructionId ir_instruction_id(IrInstructionAlloca *) {
375 return IrInstructionIdAlloca;
376}
377
374378template<typename T>
375379static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
376380 T *special_instruction = allocate<T>(1);
......@@ -1489,6 +1493,27 @@ static IrInstruction *ir_build_bool_not_from(IrBuilder *irb, IrInstruction *old_
14891493 return new_instruction;
14901494}
14911495
1496static IrInstruction *ir_build_alloca(IrBuilder *irb, Scope *scope, AstNode *source_node,
1497 IrInstruction *type_value, IrInstruction *count)
1498{
1499 IrInstructionAlloca *instruction = ir_build_instruction<IrInstructionAlloca>(irb, scope, source_node);
1500 instruction->type_value = type_value;
1501 instruction->count = count;
1502
1503 ir_ref_instruction(type_value);
1504 ir_ref_instruction(count);
1505
1506 return &instruction->base;
1507}
1508
1509static IrInstruction *ir_build_alloca_from(IrBuilder *irb, IrInstruction *old_instruction,
1510 IrInstruction *type_value, IrInstruction *count)
1511{
1512 IrInstruction *new_instruction = ir_build_alloca(irb, old_instruction->scope, old_instruction->source_node, type_value, count);
1513 ir_link_new_instruction(new_instruction, old_instruction);
1514 return new_instruction;
1515}
1516
14921517static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope,
14931518 bool gen_error_defers, bool gen_maybe_defers)
14941519{
......@@ -2310,6 +2335,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
23102335
23112336 return ir_build_int_type(irb, scope, node, arg0_value, arg1_value);
23122337 }
2338 case BuiltinFnIdAlloca:
2339 {
2340 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
2341 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
2342 if (arg0_value == irb->codegen->invalid_instruction)
2343 return arg0_value;
2344
2345 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
2346 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
2347 if (arg1_value == irb->codegen->invalid_instruction)
2348 return arg1_value;
2349
2350 return ir_build_alloca(irb, scope, node, arg0_value, arg1_value);
2351 }
23132352 case BuiltinFnIdMemcpy:
23142353 case BuiltinFnIdMemset:
23152354 case BuiltinFnIdAlignof:
......@@ -7876,6 +7915,69 @@ static TypeTableEntry *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstruc
78767915 return bool_type;
78777916}
78787917
7918static TypeTableEntry *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAlloca *instruction) {
7919 IrInstruction *type_value = instruction->type_value->other;
7920 if (type_value->type_entry->id == TypeTableEntryIdInvalid)
7921 return ira->codegen->builtin_types.entry_invalid;
7922
7923 IrInstruction *count_value = instruction->count->other;
7924 if (count_value->type_entry->id == TypeTableEntryIdInvalid)
7925 return ira->codegen->builtin_types.entry_invalid;
7926
7927 TypeTableEntry *child_type = ir_resolve_type(ira, type_value);
7928 TypeTableEntry *canon_type = get_underlying_type(child_type);
7929
7930 if (count_value->static_value.special == ConstValSpecialStatic) {
7931 // this should be the same as an array declaration
7932
7933 uint64_t count;
7934 if (!ir_resolve_usize(ira, count_value, &count))
7935 return ira->codegen->builtin_types.entry_invalid;
7936
7937 zig_panic("TODO alloca with compile time known count");
7938 }
7939
7940 switch (canon_type->id) {
7941 case TypeTableEntryIdInvalid:
7942 case TypeTableEntryIdTypeDecl:
7943 zig_unreachable();
7944 case TypeTableEntryIdBool:
7945 case TypeTableEntryIdVoid:
7946 case TypeTableEntryIdInt:
7947 case TypeTableEntryIdFloat:
7948 case TypeTableEntryIdPointer:
7949 case TypeTableEntryIdArray:
7950 case TypeTableEntryIdStruct:
7951 case TypeTableEntryIdMaybe:
7952 case TypeTableEntryIdErrorUnion:
7953 case TypeTableEntryIdPureError:
7954 case TypeTableEntryIdEnum:
7955 case TypeTableEntryIdUnion:
7956 case TypeTableEntryIdFn:
7957 {
7958 TypeTableEntry *slice_type = get_slice_type(ira->codegen, child_type, false);
7959 IrInstruction *new_instruction = ir_build_alloca_from(&ira->new_irb, &instruction->base, type_value, count_value);
7960 ir_add_alloca(ira, new_instruction, slice_type);
7961 return slice_type;
7962 }
7963 case TypeTableEntryIdVar:
7964 case TypeTableEntryIdMetaType:
7965 case TypeTableEntryIdUnreachable:
7966 case TypeTableEntryIdNumLitFloat:
7967 case TypeTableEntryIdNumLitInt:
7968 case TypeTableEntryIdUndefLit:
7969 case TypeTableEntryIdNullLit:
7970 case TypeTableEntryIdNamespace:
7971 case TypeTableEntryIdBlock:
7972 case TypeTableEntryIdBoundFn:
7973 ir_add_error(ira, type_value,
7974 buf_sprintf("invalid alloca type '%s'", buf_ptr(&child_type->name)));
7975 // TODO if this is a typedecl, add error note showing the declaration of the type decl
7976 return ira->codegen->builtin_types.entry_invalid;
7977 }
7978 zig_unreachable();
7979}
7980
78797981static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
78807982 switch (instruction->id) {
78817983 case IrInstructionIdInvalid:
......@@ -7990,6 +8092,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
79908092 return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction);
79918093 case IrInstructionIdBoolNot:
79928094 return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction);
8095 case IrInstructionIdAlloca:
8096 return ir_analyze_instruction_alloca(ira, (IrInstructionAlloca *)instruction);
79938097 case IrInstructionIdCast:
79948098 case IrInstructionIdStructFieldPtr:
79958099 case IrInstructionIdEnumFieldPtr:
......@@ -8131,6 +8235,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
81318235 case IrInstructionIdTruncate:
81328236 case IrInstructionIdIntType:
81338237 case IrInstructionIdBoolNot:
8238 case IrInstructionIdAlloca:
81348239 return false;
81358240 case IrInstructionIdAsm:
81368241 {
......@@ -8998,115 +9103,3 @@ bool ir_has_side_effects(IrInstruction *instruction) {
89989103// }
89999104// zig_unreachable();
90009105//}
9001//
9002//static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl,
9003// bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type, bool var_is_ptr)
9004//{
9005// VariableTableEntry *variable = var_decl->variable;
9006//
9007// assert(variable);
9008//
9009// if (var_decl->expr) {
9010// *init_value = gen_expr(g, var_decl->expr);
9011// *expr_type = get_expr_type(var_decl->expr);
9012// }
9013// if (!type_has_bits(variable->type)) {
9014// return nullptr;
9015// }
9016//
9017// bool have_init_expr = false;
9018// bool want_zeroes = false;
9019// if (var_decl->expr) {
9020// ConstExprValue *const_val = &get_resolved_expr(var_decl->expr)->const_val;
9021// if (!const_val->ok || const_val->special == ConstValSpecialOther) {
9022// have_init_expr = true;
9023// }
9024// if (const_val->ok && const_val->special == ConstValSpecialZeroes) {
9025// want_zeroes = true;
9026// }
9027// }
9028// if (have_init_expr) {
9029// TypeTableEntry *expr_type = get_expr_type(var_decl->expr);
9030// LLVMValueRef value;
9031// if (unwrap_maybe) {
9032// assert(var_decl->expr);
9033// assert(expr_type->id == TypeTableEntryIdMaybe);
9034// value = gen_unwrap_maybe(g, var_decl->expr, *init_value);
9035// expr_type = expr_type->data.maybe.child_type;
9036// } else {
9037// value = *init_value;
9038// }
9039// gen_assign_raw(g, var_decl->expr, BinOpTypeAssign, variable->value_ref,
9040// value, variable->type, expr_type);
9041// } else {
9042// bool ignore_uninit = false;
9043// // handle runtime stack allocation
9044// if (var_decl->type) {
9045// TypeTableEntry *var_type = get_type_for_type_node(var_decl->type);
9046// if (var_type->id == TypeTableEntryIdStruct &&
9047// var_type->data.structure.is_slice)
9048// {
9049// assert(var_decl->type->type == NodeTypeArrayType);
9050// AstNode *size_node = var_decl->type->data.array_type.size;
9051// if (size_node) {
9052// ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
9053// if (!const_val->ok) {
9054// TypeTableEntry *ptr_type = var_type->data.structure.fields[0].type_entry;
9055// assert(ptr_type->id == TypeTableEntryIdPointer);
9056// TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
9057//
9058// LLVMValueRef size_val = gen_expr(g, size_node);
9059//
9060// set_debug_source_node(g, source_node);
9061// LLVMValueRef ptr_val = LLVMBuildArrayAlloca(g->builder, child_type->type_ref,
9062// size_val, "");
9063//
9064// size_t ptr_index = var_type->data.structure.fields[0].gen_index;
9065// assert(ptr_index != SIZE_MAX);
9066// size_t len_index = var_type->data.structure.fields[1].gen_index;
9067// assert(len_index != SIZE_MAX);
9068//
9069// // store the freshly allocated pointer in the unknown size array struct
9070// LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder,
9071// variable->value_ref, ptr_index, "");
9072// LLVMBuildStore(g->builder, ptr_val, ptr_field_ptr);
9073//
9074// // store the size in the len field
9075// LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder,
9076// variable->value_ref, len_index, "");
9077// LLVMBuildStore(g->builder, size_val, len_field_ptr);
9078//
9079// // don't clobber what we just did with debug initialization
9080// ignore_uninit = true;
9081// }
9082// }
9083// }
9084// }
9085// bool want_safe = want_debug_safety(g, source_node);
9086// if (!ignore_uninit && (want_safe || want_zeroes)) {
9087// TypeTableEntry *usize = g->builtin_types.entry_usize;
9088// uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref);
9089// uint64_t align_bytes = get_memcpy_align(g, variable->type);
9090//
9091// // memset uninitialized memory to 0xa
9092// set_debug_source_node(g, source_node);
9093// LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
9094// LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), want_zeroes ? 0x00 : 0xaa, false);
9095// LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, variable->value_ref, ptr_u8, "");
9096// LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false);
9097// LLVMValueRef align_in_bytes = LLVMConstInt(LLVMInt32Type(), align_bytes, false);
9098// LLVMValueRef params[] = {
9099// dest_ptr,
9100// fill_char,
9101// byte_count,
9102// align_in_bytes,
9103// LLVMConstNull(LLVMInt1Type()), // is volatile
9104// };
9105//
9106// LLVMBuildCall(g->builder, g->memset_fn_val, params, 5, "");
9107// }
9108// }
9109//
9110// gen_var_debug_decl(g, variable);
9111// return nullptr;
9112//}
src/ir_print.cpp+11
......@@ -753,6 +753,14 @@ static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction)
753753 fprintf(irp->f, ")");
754754}
755755
756static void ir_print_alloca(IrPrint *irp, IrInstructionAlloca *instruction) {
757 fprintf(irp->f, "@alloca(");
758 ir_print_other_instruction(irp, instruction->type_value);
759 fprintf(irp->f, ", ");
760 ir_print_other_instruction(irp, instruction->count);
761 fprintf(irp->f, ")");
762}
763
756764static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) {
757765 fprintf(irp->f, "@intType(");
758766 ir_print_other_instruction(irp, instruction->is_signed);
......@@ -942,6 +950,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
942950 case IrInstructionIdTruncate:
943951 ir_print_truncate(irp, (IrInstructionTruncate *)instruction);
944952 break;
953 case IrInstructionIdAlloca:
954 ir_print_alloca(irp, (IrInstructionAlloca *)instruction);
955 break;
945956 case IrInstructionIdIntType:
946957 ir_print_int_type(irp, (IrInstructionIntType *)instruction);
947958 break;