authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-24 20:24:05-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-24 20:24:05-04:00
logadefd1a52b812813dd3e3590d398f927ffc5b9af
tree1971356295aa4dca151b49ff03e7e2ce10e4a345
parent2ea08561cf69dabc99722ffc24cb0e4327605506

self-hosted: function calling another function


7 files changed, 597 insertions(+), 175 deletions(-)

src-self-hosted/codegen.zig+157-3
......@@ -6,6 +6,7 @@ const c = @import("c.zig");
66const ir = @import("ir.zig");
77const Value = @import("value.zig").Value;
88const Type = @import("type.zig").Type;
9const Scope = @import("scope.zig").Scope;
910const event = std.event;
1011const assert = std.debug.assert;
1112const DW = std.dwarf;
......@@ -156,7 +157,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
156157 llvm_fn_type,
157158 ) orelse return error.OutOfMemory;
158159
159 const want_fn_safety = fn_val.block_scope.safety.get(ofile.comp);
160 const want_fn_safety = fn_val.block_scope.?.safety.get(ofile.comp);
160161 if (want_fn_safety and ofile.comp.haveLibC()) {
161162 try addLLVMFnAttr(ofile, llvm_fn, "sspstrong");
162163 try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4");
......@@ -227,9 +228,86 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
227228
228229 // TODO set up error return tracing
229230 // TODO allocate temporary stack values
230 // TODO create debug variable declarations for variables and allocate all local variables
231
232 const var_list = fn_type.non_key.Normal.variable_list.toSliceConst();
233 // create debug variable declarations for variables and allocate all local variables
234 for (var_list) |var_scope, i| {
235 const var_type = switch (var_scope.data) {
236 Scope.Var.Data.Const => unreachable,
237 Scope.Var.Data.Param => |param| param.typ,
238 };
239 // if (!type_has_bits(var->value->type)) {
240 // continue;
241 // }
242 // if (ir_get_var_is_comptime(var))
243 // continue;
244 // if (type_requires_comptime(var->value->type))
245 // continue;
246 // if (var->src_arg_index == SIZE_MAX) {
247 // var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes);
248
249 // var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
250 // buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1),
251 // var->value->type->di_type, !g->strip_debug_symbols, 0);
252
253 // } else {
254 // it's a parameter
255 // assert(var->gen_arg_index != SIZE_MAX);
256 // TypeTableEntry *gen_type;
257 // FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index];
258
259 if (var_type.handleIsPtr()) {
260 // if (gen_info->is_byval) {
261 // gen_type = var->value->type;
262 // } else {
263 // gen_type = gen_info->type;
264 // }
265 var_scope.data.Param.llvm_value = llvm.GetParam(llvm_fn, @intCast(c_uint, i));
266 } else {
267 // gen_type = var->value->type;
268 var_scope.data.Param.llvm_value = try renderAlloca(ofile, var_type, var_scope.name, Type.Pointer.Align.Abi);
269 }
270 // if (var->decl_node) {
271 // var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
272 // buf_ptr(&var->name), import->di_file,
273 // (unsigned)(var->decl_node->line + 1),
274 // gen_type->di_type, !g->strip_debug_symbols, 0, (unsigned)(var->gen_arg_index + 1));
275 // }
276
277 // }
278 }
279
231280 // TODO finishing error return trace setup. we have to do this after all the allocas.
232 // TODO create debug variable declarations for parameters
281
282 // create debug variable declarations for parameters
283 // rely on the first variables in the variable_list being parameters.
284 //size_t next_var_i = 0;
285 for (fn_type.key.data.Normal.params) |param, i| {
286 //FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i];
287 //if (info->gen_index == SIZE_MAX)
288 // continue;
289 const scope_var = var_list[i];
290 //assert(variable->src_arg_index != SIZE_MAX);
291 //next_var_i += 1;
292 //assert(variable);
293 //assert(variable->value_ref);
294
295 if (!param.typ.handleIsPtr()) {
296 //clear_debug_source_node(g);
297 const llvm_param = llvm.GetParam(llvm_fn, @intCast(c_uint, i));
298 _ = renderStoreUntyped(
299 ofile,
300 llvm_param,
301 scope_var.data.Param.llvm_value,
302 Type.Pointer.Align.Abi,
303 Type.Pointer.Vol.Non,
304 );
305 }
306
307 //if (variable->decl_node) {
308 // gen_var_debug_decl(g, variable);
309 //}
310 }
233311
234312 for (code.basic_block_list.toSlice()) |current_block| {
235313 llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block);
......@@ -294,3 +372,79 @@ fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []cons
294372fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void {
295373 return addLLVMAttrInt(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val);
296374}
375
376fn renderLoadUntyped(
377 ofile: *ObjectFile,
378 ptr: llvm.ValueRef,
379 alignment: Type.Pointer.Align,
380 vol: Type.Pointer.Vol,
381 name: [*]const u8,
382) !llvm.ValueRef {
383 const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory;
384 switch (vol) {
385 Type.Pointer.Vol.Non => {},
386 Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1),
387 }
388 llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.GetElementType(llvm.TypeOf(ptr))));
389 return result;
390}
391
392fn renderLoad(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer, name: [*]const u8) !llvm.ValueRef {
393 return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name);
394}
395
396pub fn getHandleValue(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer) !?llvm.ValueRef {
397 const child_type = ptr_type.key.child_type;
398 if (!child_type.hasBits()) {
399 return null;
400 }
401 if (child_type.handleIsPtr()) {
402 return ptr;
403 }
404 return try renderLoad(ofile, ptr, ptr_type, c"");
405}
406
407pub fn renderStoreUntyped(
408 ofile: *ObjectFile,
409 value: llvm.ValueRef,
410 ptr: llvm.ValueRef,
411 alignment: Type.Pointer.Align,
412 vol: Type.Pointer.Vol,
413) !llvm.ValueRef {
414 const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory;
415 switch (vol) {
416 Type.Pointer.Vol.Non => {},
417 Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1),
418 }
419 llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.TypeOf(value)));
420 return result;
421}
422
423pub fn renderStore(
424 ofile: *ObjectFile,
425 value: llvm.ValueRef,
426 ptr: llvm.ValueRef,
427 ptr_type: *Type.Pointer,
428) !llvm.ValueRef {
429 return renderStoreUntyped(ofile, value, ptr, ptr_type.key.alignment, ptr_type.key.vol);
430}
431
432pub fn renderAlloca(
433 ofile: *ObjectFile,
434 var_type: *Type,
435 name: []const u8,
436 alignment: Type.Pointer.Align,
437) !llvm.ValueRef {
438 const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context);
439 const name_with_null = try std.cstr.addNullByte(ofile.arena, name);
440 const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory;
441 llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type));
442 return result;
443}
444
445pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: llvm.TypeRef) u32 {
446 return switch (alignment) {
447 Type.Pointer.Align.Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type),
448 Type.Pointer.Align.Override => |a| a,
449 };
450}
src-self-hosted/compilation.zig+35-37
......@@ -1165,49 +1165,47 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
11651165 symbol_name_consumed = true;
11661166
11671167 // Define local parameter variables
1168 //for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
1169 // FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
1170 // AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
1171 // Buf *param_name;
1172 // bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args;
1173 // if (param_decl_node && !is_var_args) {
1174 // param_name = param_decl_node->data.param_decl.name;
1175 // } else {
1176 // param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
1177 // }
1178 // if (param_name == nullptr) {
1179 // continue;
1180 // }
1181
1182 // TypeTableEntry *param_type = param_info->type;
1183 // bool is_noalias = param_info->is_noalias;
1184
1185 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
1186 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
1187 // }
1188
1189 // VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope,
1190 // param_name, true, create_const_runtime(param_type), nullptr);
1191 // var->src_arg_index = i;
1192 // fn_table_entry->child_scope = var->child_scope;
1193 // var->shadowable = var->shadowable || is_var_args;
1194
1195 // if (type_has_bits(param_type)) {
1196 // fn_table_entry->variable_list.append(var);
1197 // }
1198
1199 // if (fn_type->data.fn.gen_param_info) {
1200 // var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
1201 // }
1202 //}
1168 const root_scope = fn_decl.base.findRootScope();
1169 for (fn_type.key.data.Normal.params) |param, i| {
1170 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
1171 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
1172 const name_token = param_decl.name_token orelse {
1173 try comp.addCompileError(root_scope, Span{
1174 .first = param_decl.firstToken(),
1175 .last = param_decl.type_node.firstToken(),
1176 }, "missing parameter name");
1177 return error.SemanticAnalysisFailed;
1178 };
1179 const param_name = root_scope.tree.tokenSlice(name_token);
1180
1181 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
1182 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
1183 // }
1184
1185 // TODO check for shadowing
1186
1187 const var_scope = try Scope.Var.createParam(
1188 comp,
1189 fn_val.child_scope,
1190 param_name,
1191 &param_decl.base,
1192 i,
1193 param.typ,
1194 );
1195 fn_val.child_scope = &var_scope.base;
1196
1197 try fn_type.non_key.Normal.variable_list.append(var_scope);
1198 }
12031199
12041200 const analyzed_code = try await (async comp.genAndAnalyzeCode(
1205 &fndef_scope.base,
1201 fn_val.child_scope,
12061202 body_node,
12071203 fn_type.key.data.Normal.return_type,
12081204 ) catch unreachable);
12091205 errdefer analyzed_code.destroy(comp.gpa());
12101206
1207 assert(fn_val.block_scope != null);
1208
12111209 // Kick off rendering to LLVM module, but it doesn't block the fn decl
12121210 // analysis from being complete.
12131211 try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
......@@ -1263,7 +1261,7 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
12631261 const key = Type.Fn.Key{
12641262 .alignment = null,
12651263 .data = Type.Fn.Key.Data{
1266 .Normal = Type.Fn.Normal{
1264 .Normal = Type.Fn.Key.Normal{
12671265 .return_type = return_type,
12681266 .params = params.toOwnedSlice(),
12691267 .is_var_args = false, // TODO
src-self-hosted/ir.zig+176-21
......@@ -10,8 +10,10 @@ const assert = std.debug.assert;
1010const Token = std.zig.Token;
1111const Span = @import("errmsg.zig").Span;
1212const llvm = @import("llvm.zig");
13const ObjectFile = @import("codegen.zig").ObjectFile;
13const codegen = @import("codegen.zig");
14const ObjectFile = codegen.ObjectFile;
1415const Decl = @import("decl.zig").Decl;
16const mem = std.mem;
1517
1618pub const LVal = enum {
1719 None,
......@@ -122,6 +124,8 @@ pub const Inst = struct {
122124 Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira),
123125 Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira),
124126 Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable),
127 Id.VarPtr => return await (async @fieldParentPtr(VarPtr, "base", base).analyze(ira) catch unreachable),
128 Id.LoadPtr => return await (async @fieldParentPtr(LoadPtr, "base", base).analyze(ira) catch unreachable),
125129 }
126130 }
127131
......@@ -130,6 +134,8 @@ pub const Inst = struct {
130134 Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
131135 Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),
132136 Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val),
137 Id.VarPtr => return @fieldParentPtr(VarPtr, "base", base).render(ofile, fn_val),
138 Id.LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).render(ofile, fn_val),
133139 Id.DeclRef => unreachable,
134140 Id.PtrType => unreachable,
135141 Id.Ref => @panic("TODO"),
......@@ -248,6 +254,8 @@ pub const Inst = struct {
248254 Call,
249255 DeclRef,
250256 PtrType,
257 VarPtr,
258 LoadPtr,
251259 };
252260
253261 pub const Call = struct {
......@@ -491,6 +499,133 @@ pub const Inst = struct {
491499 }
492500 };
493501
502 pub const VarPtr = struct {
503 base: Inst,
504 params: Params,
505
506 const Params = struct {
507 var_scope: *Scope.Var,
508 };
509
510 const ir_val_init = IrVal.Init.Unknown;
511
512 pub fn dump(inst: *const VarPtr) void {
513 std.debug.warn("{}", inst.params.var_scope.name);
514 }
515
516 pub fn hasSideEffects(inst: *const VarPtr) bool {
517 return false;
518 }
519
520 pub async fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {
521 switch (self.params.var_scope.data) {
522 Scope.Var.Data.Const => @panic("TODO"),
523 Scope.Var.Data.Param => |param| {
524 const new_inst = try ira.irb.build(
525 Inst.VarPtr,
526 self.base.scope,
527 self.base.span,
528 Inst.VarPtr.Params{ .var_scope = self.params.var_scope },
529 );
530 const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
531 .child_type = param.typ,
532 .mut = Type.Pointer.Mut.Const,
533 .vol = Type.Pointer.Vol.Non,
534 .size = Type.Pointer.Size.One,
535 .alignment = Type.Pointer.Align.Abi,
536 }) catch unreachable);
537 new_inst.val = IrVal{ .KnownType = &ptr_type.base };
538 return new_inst;
539 },
540 }
541 }
542
543 pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) llvm.ValueRef {
544 switch (self.params.var_scope.data) {
545 Scope.Var.Data.Const => unreachable, // turned into Inst.Const in analyze pass
546 Scope.Var.Data.Param => |param| return param.llvm_value,
547 }
548 }
549 };
550
551 pub const LoadPtr = struct {
552 base: Inst,
553 params: Params,
554
555 const Params = struct {
556 target: *Inst,
557 };
558
559 const ir_val_init = IrVal.Init.Unknown;
560
561 pub fn dump(inst: *const LoadPtr) void {}
562
563 pub fn hasSideEffects(inst: *const LoadPtr) bool {
564 return false;
565 }
566
567 pub async fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst {
568 const target = try self.params.target.getAsParam();
569 const target_type = target.getKnownType();
570 if (target_type.id != Type.Id.Pointer) {
571 try ira.addCompileError(self.base.span, "dereference of non pointer type '{}'", target_type.name);
572 return error.SemanticAnalysisFailed;
573 }
574 const ptr_type = @fieldParentPtr(Type.Pointer, "base", target_type);
575 // if (instr_is_comptime(ptr)) {
576 // if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst ||
577 // ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
578 // {
579 // ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &ptr->value);
580 // if (pointee->special != ConstValSpecialRuntime) {
581 // IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,
582 // source_instruction->source_node, child_type);
583 // copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst);
584 // result->value.type = child_type;
585 // return result;
586 // }
587 // }
588 // }
589 const new_inst = try ira.irb.build(
590 Inst.LoadPtr,
591 self.base.scope,
592 self.base.span,
593 Inst.LoadPtr.Params{ .target = target },
594 );
595 new_inst.val = IrVal{ .KnownType = ptr_type.key.child_type };
596 return new_inst;
597 }
598
599 pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
600 const child_type = self.base.getKnownType();
601 if (!child_type.hasBits()) {
602 return null;
603 }
604 const ptr = self.params.target.llvm_value.?;
605 const ptr_type = self.params.target.getKnownType().cast(Type.Pointer).?;
606
607 return try codegen.getHandleValue(ofile, ptr, ptr_type);
608
609 //uint32_t unaligned_bit_count = ptr_type->data.pointer.unaligned_bit_count;
610 //if (unaligned_bit_count == 0)
611 // return get_handle_value(g, ptr, child_type, ptr_type);
612
613 //bool big_endian = g->is_big_endian;
614
615 //assert(!handle_is_ptr(child_type));
616 //LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, "");
617
618 //uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
619 //uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
620 //uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset;
621
622 //LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
623 //LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, "");
624
625 //return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
626 }
627 };
628
494629 pub const PtrType = struct {
495630 base: Inst,
496631 params: Params,
......@@ -1160,6 +1295,7 @@ pub const Builder = struct {
11601295 Scope.Id.Block,
11611296 Scope.Id.Defer,
11621297 Scope.Id.DeferExpr,
1298 Scope.Id.Var,
11631299 => scope = scope.parent.?,
11641300 }
11651301 }
......@@ -1261,8 +1397,8 @@ pub const Builder = struct {
12611397 var child_scope = outer_block_scope;
12621398
12631399 if (parent_scope.findFnDef()) |fndef_scope| {
1264 if (fndef_scope.fn_val.child_scope == parent_scope) {
1265 fndef_scope.fn_val.block_scope = block_scope;
1400 if (fndef_scope.fn_val.?.block_scope == null) {
1401 fndef_scope.fn_val.?.block_scope = block_scope;
12661402 }
12671403 }
12681404
......@@ -1492,20 +1628,23 @@ pub const Builder = struct {
14921628 error.OutOfMemory => return error.OutOfMemory,
14931629 }
14941630
1495 //VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);
1496 //if (var) {
1497 // IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var);
1498 // if (lval == LValPtr)
1499 // return var_ptr;
1500 // else
1501 // return ir_build_load_ptr(irb, scope, node, var_ptr);
1502 //}
1503
1504 if (await (async irb.findDecl(scope, name) catch unreachable)) |decl| {
1505 return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
1506 .decl = decl,
1507 .lval = lval,
1508 });
1631 switch (await (async irb.findIdent(scope, name) catch unreachable)) {
1632 Ident.Decl => |decl| {
1633 return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
1634 .decl = decl,
1635 .lval = lval,
1636 });
1637 },
1638 Ident.VarScope => |var_scope| {
1639 const var_ptr = try irb.build(Inst.VarPtr, scope, src_span, Inst.VarPtr.Params{ .var_scope = var_scope });
1640 switch (lval) {
1641 LVal.Ptr => return var_ptr,
1642 LVal.None => {
1643 return irb.build(Inst.LoadPtr, scope, src_span, Inst.LoadPtr.Params{ .target = var_ptr });
1644 },
1645 }
1646 },
1647 Ident.NotFound => {},
15091648 }
15101649
15111650 //if (node->owner->any_imports_failed) {
......@@ -1546,6 +1685,7 @@ pub const Builder = struct {
15461685 Scope.Id.Block,
15471686 Scope.Id.Decls,
15481687 Scope.Id.Root,
1688 Scope.Id.Var,
15491689 => scope = scope.parent orelse break,
15501690
15511691 Scope.Id.DeferExpr => unreachable,
......@@ -1596,6 +1736,7 @@ pub const Builder = struct {
15961736
15971737 Scope.Id.CompTime,
15981738 Scope.Id.Block,
1739 Scope.Id.Var,
15991740 => scope = scope.parent orelse return is_noreturn,
16001741
16011742 Scope.Id.DeferExpr => unreachable,
......@@ -1674,8 +1815,10 @@ pub const Builder = struct {
16741815 Type.Pointer.Size,
16751816 LVal,
16761817 *Decl,
1818 *Scope.Var,
16771819 => {},
1678 // it's ok to add more types here, just make sure any instructions are ref'd appropriately
1820 // it's ok to add more types here, just make sure that
1821 // any instructions and basic blocks are ref'd appropriately
16791822 else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)),
16801823 }
16811824 }
......@@ -1773,18 +1916,30 @@ pub const Builder = struct {
17731916 //// the above blocks are rendered by ir_gen after the rest of codegen
17741917 }
17751918
1776 async fn findDecl(irb: *Builder, scope: *Scope, name: []const u8) ?*Decl {
1919 const Ident = union(enum) {
1920 NotFound,
1921 Decl: *Decl,
1922 VarScope: *Scope.Var,
1923 };
1924
1925 async fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident {
17771926 var s = scope;
17781927 while (true) {
17791928 switch (s.id) {
1929 Scope.Id.Root => return Ident.NotFound,
17801930 Scope.Id.Decls => {
17811931 const decls = @fieldParentPtr(Scope.Decls, "base", s);
17821932 const table = await (async decls.getTableReadOnly() catch unreachable);
17831933 if (table.get(name)) |entry| {
1784 return entry.value;
1934 return Ident{ .Decl = entry.value };
1935 }
1936 },
1937 Scope.Id.Var => {
1938 const var_scope = @fieldParentPtr(Scope.Var, "base", s);
1939 if (mem.eql(u8, var_scope.name, name)) {
1940 return Ident{ .VarScope = var_scope };
17851941 }
17861942 },
1787 Scope.Id.Root => return null,
17881943 else => {},
17891944 }
17901945 s = s.parent.?;
src-self-hosted/llvm.zig+14-1
......@@ -30,6 +30,7 @@ pub const AddGlobal = c.LLVMAddGlobal;
3030pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
3131pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
3232pub const ArrayType = c.LLVMArrayType;
33pub const BuildLoad = c.LLVMBuildLoad;
3334pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
3435pub const ConstAllOnes = c.LLVMConstAllOnes;
3536pub const ConstArray = c.LLVMConstArray;
......@@ -95,13 +96,25 @@ pub const SetInitializer = c.LLVMSetInitializer;
9596pub const SetLinkage = c.LLVMSetLinkage;
9697pub const SetTarget = c.LLVMSetTarget;
9798pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
99pub const SetVolatile = c.LLVMSetVolatile;
98100pub const StructTypeInContext = c.LLVMStructTypeInContext;
99101pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
100pub const TypeOf = c.LLVMTypeOf;
101102pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
102103pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
103104pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
104105
106pub const GetElementType = LLVMGetElementType;
107extern fn LLVMGetElementType(Ty: TypeRef) TypeRef;
108
109pub const TypeOf = LLVMTypeOf;
110extern fn LLVMTypeOf(Val: ValueRef) TypeRef;
111
112pub const BuildStore = LLVMBuildStore;
113extern fn LLVMBuildStore(arg0: BuilderRef, Val: ValueRef, Ptr: ValueRef) ?ValueRef;
114
115pub const BuildAlloca = LLVMBuildAlloca;
116extern fn LLVMBuildAlloca(arg0: BuilderRef, Ty: TypeRef, Name: ?[*]const u8) ?ValueRef;
117
105118pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
106119pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
107120
src-self-hosted/scope.zig+128-73
......@@ -6,23 +6,26 @@ const Compilation = @import("compilation.zig").Compilation;
66const mem = std.mem;
77const ast = std.zig.ast;
88const Value = @import("value.zig").Value;
9const Type = @import("type.zig").Type;
910const ir = @import("ir.zig");
1011const Span = @import("errmsg.zig").Span;
1112const assert = std.debug.assert;
1213const event = std.event;
14const llvm = @import("llvm.zig");
1315
1416pub const Scope = struct {
1517 id: Id,
1618 parent: ?*Scope,
17 ref_count: usize,
19 ref_count: std.atomic.Int(usize),
1820
21 /// Thread-safe
1922 pub fn ref(base: *Scope) void {
20 base.ref_count += 1;
23 _ = base.ref_count.incr();
2124 }
2225
26 /// Thread-safe
2327 pub fn deref(base: *Scope, comp: *Compilation) void {
24 base.ref_count -= 1;
25 if (base.ref_count == 0) {
28 if (base.ref_count.decr() == 1) {
2629 if (base.parent) |parent| parent.deref(comp);
2730 switch (base.id) {
2831 Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp),
......@@ -32,6 +35,7 @@ pub const Scope = struct {
3235 Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
3336 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
3437 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
38 Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),
3539 }
3640 }
3741 }
......@@ -49,15 +53,15 @@ pub const Scope = struct {
4953 var scope = base;
5054 while (true) {
5155 switch (scope.id) {
52 Id.FnDef => return @fieldParentPtr(FnDef, "base", base),
53 Id.Decls => return null,
56 Id.FnDef => return @fieldParentPtr(FnDef, "base", scope),
57 Id.Root, Id.Decls => return null,
5458
5559 Id.Block,
5660 Id.Defer,
5761 Id.DeferExpr,
5862 Id.CompTime,
59 Id.Root,
60 => scope = scope.parent orelse return null,
63 Id.Var,
64 => scope = scope.parent.?,
6165 }
6266 }
6367 }
......@@ -66,7 +70,7 @@ pub const Scope = struct {
6670 var scope = base;
6771 while (true) {
6872 switch (scope.id) {
69 Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base),
73 Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope),
7074
7175 Id.FnDef,
7276 Id.Decls,
......@@ -76,11 +80,21 @@ pub const Scope = struct {
7680 Id.Defer,
7781 Id.CompTime,
7882 Id.Root,
83 Id.Var,
7984 => scope = scope.parent orelse return null,
8085 }
8186 }
8287 }
8388
89 fn init(base: *Scope, id: Id, parent: *Scope) void {
90 base.* = Scope{
91 .id = id,
92 .parent = parent,
93 .ref_count = std.atomic.Int(usize).init(1),
94 };
95 parent.ref();
96 }
97
8498 pub const Id = enum {
8599 Root,
86100 Decls,
......@@ -89,6 +103,7 @@ pub const Scope = struct {
89103 CompTime,
90104 Defer,
91105 DeferExpr,
106 Var,
92107 };
93108
94109 pub const Root = struct {
......@@ -100,16 +115,16 @@ pub const Scope = struct {
100115 /// Takes ownership of realpath
101116 /// Takes ownership of tree, will deinit and destroy when done.
102117 pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
103 const self = try comp.gpa().create(Root{
118 const self = try comp.gpa().createOne(Root);
119 self.* = Root{
104120 .base = Scope{
105121 .id = Id.Root,
106122 .parent = null,
107 .ref_count = 1,
123 .ref_count = std.atomic.Int(usize).init(1),
108124 },
109125 .tree = tree,
110126 .realpath = realpath,
111 });
112 errdefer comp.gpa().destroy(self);
127 };
113128
114129 return self;
115130 }
......@@ -137,16 +152,13 @@ pub const Scope = struct {
137152
138153 /// Creates a Decls scope with 1 reference
139154 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
140 const self = try comp.gpa().create(Decls{
141 .base = Scope{
142 .id = Id.Decls,
143 .parent = parent,
144 .ref_count = 1,
145 },
155 const self = try comp.gpa().createOne(Decls);
156 self.* = Decls{
157 .base = undefined,
146158 .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
147159 .name_future = event.Future(void).init(comp.loop),
148 });
149 parent.ref();
160 };
161 self.base.init(Id.Decls, parent);
150162 return self;
151163 }
152164
......@@ -199,21 +211,16 @@ pub const Scope = struct {
199211
200212 /// Creates a Block scope with 1 reference
201213 pub fn create(comp: *Compilation, parent: *Scope) !*Block {
202 const self = try comp.gpa().create(Block{
203 .base = Scope{
204 .id = Id.Block,
205 .parent = parent,
206 .ref_count = 1,
207 },
214 const self = try comp.gpa().createOne(Block);
215 self.* = Block{
216 .base = undefined,
208217 .incoming_values = undefined,
209218 .incoming_blocks = undefined,
210219 .end_block = undefined,
211220 .is_comptime = undefined,
212221 .safety = Safety.Auto,
213 });
214 errdefer comp.gpa().destroy(self);
215
216 parent.ref();
222 };
223 self.base.init(Id.Block, parent);
217224 return self;
218225 }
219226
......@@ -226,22 +233,17 @@ pub const Scope = struct {
226233 base: Scope,
227234
228235 /// This reference is not counted so that the scope can get destroyed with the function
229 fn_val: *Value.Fn,
236 fn_val: ?*Value.Fn,
230237
231238 /// Creates a FnDef scope with 1 reference
232239 /// Must set the fn_val later
233240 pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
234 const self = try comp.gpa().create(FnDef{
235 .base = Scope{
236 .id = Id.FnDef,
237 .parent = parent,
238 .ref_count = 1,
239 },
240 .fn_val = undefined,
241 });
242
243 parent.ref();
244
241 const self = try comp.gpa().createOne(FnDef);
242 self.* = FnDef{
243 .base = undefined,
244 .fn_val = null,
245 };
246 self.base.init(Id.FnDef, parent);
245247 return self;
246248 }
247249
......@@ -255,15 +257,9 @@ pub const Scope = struct {
255257
256258 /// Creates a CompTime scope with 1 reference
257259 pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
258 const self = try comp.gpa().create(CompTime{
259 .base = Scope{
260 .id = Id.CompTime,
261 .parent = parent,
262 .ref_count = 1,
263 },
264 });
265
266 parent.ref();
260 const self = try comp.gpa().createOne(CompTime);
261 self.* = CompTime{ .base = undefined };
262 self.base.init(Id.CompTime, parent);
267263 return self;
268264 }
269265
......@@ -289,20 +285,14 @@ pub const Scope = struct {
289285 kind: Kind,
290286 defer_expr_scope: *DeferExpr,
291287 ) !*Defer {
292 const self = try comp.gpa().create(Defer{
293 .base = Scope{
294 .id = Id.Defer,
295 .parent = parent,
296 .ref_count = 1,
297 },
288 const self = try comp.gpa().createOne(Defer);
289 self.* = Defer{
290 .base = undefined,
298291 .defer_expr_scope = defer_expr_scope,
299292 .kind = kind,
300 });
301 errdefer comp.gpa().destroy(self);
302
293 };
294 self.base.init(Id.Defer, parent);
303295 defer_expr_scope.base.ref();
304
305 parent.ref();
306296 return self;
307297 }
308298
......@@ -319,18 +309,13 @@ pub const Scope = struct {
319309
320310 /// Creates a DeferExpr scope with 1 reference
321311 pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
322 const self = try comp.gpa().create(DeferExpr{
323 .base = Scope{
324 .id = Id.DeferExpr,
325 .parent = parent,
326 .ref_count = 1,
327 },
312 const self = try comp.gpa().createOne(DeferExpr);
313 self.* = DeferExpr{
314 .base = undefined,
328315 .expr_node = expr_node,
329316 .reported_err = false,
330 });
331 errdefer comp.gpa().destroy(self);
332
333 parent.ref();
317 };
318 self.base.init(Id.DeferExpr, parent);
334319 return self;
335320 }
336321
......@@ -338,4 +323,74 @@ pub const Scope = struct {
338323 comp.gpa().destroy(self);
339324 }
340325 };
326
327 pub const Var = struct {
328 base: Scope,
329 name: []const u8,
330 src_node: *ast.Node,
331 data: Data,
332
333 pub const Data = union(enum) {
334 Param: Param,
335 Const: *Value,
336 };
337
338 pub const Param = struct {
339 index: usize,
340 typ: *Type,
341 llvm_value: llvm.ValueRef,
342 };
343
344 pub fn createParam(
345 comp: *Compilation,
346 parent: *Scope,
347 name: []const u8,
348 src_node: *ast.Node,
349 param_index: usize,
350 param_type: *Type,
351 ) !*Var {
352 const self = try create(comp, parent, name, src_node);
353 self.data = Data{
354 .Param = Param{
355 .index = param_index,
356 .typ = param_type,
357 .llvm_value = undefined,
358 },
359 };
360 return self;
361 }
362
363 pub fn createConst(
364 comp: *Compilation,
365 parent: *Scope,
366 name: []const u8,
367 src_node: *ast.Node,
368 value: *Value,
369 ) !*Var {
370 const self = try create(comp, parent, name, src_node);
371 self.data = Data{ .Const = value };
372 value.ref();
373 return self;
374 }
375
376 fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var {
377 const self = try comp.gpa().createOne(Var);
378 self.* = Var{
379 .base = undefined,
380 .name = name,
381 .src_node = src_node,
382 .data = undefined,
383 };
384 self.base.init(Id.Var, parent);
385 return self;
386 }
387
388 pub fn destroy(self: *Var, comp: *Compilation) void {
389 switch (self.data) {
390 Data.Param => {},
391 Data.Const => |value| value.deref(comp),
392 }
393 comp.gpa().destroy(self);
394 }
395 };
341396};
src-self-hosted/type.zig+68-37
......@@ -141,9 +141,13 @@ pub const Type = struct {
141141 Id.Promise,
142142 => return true,
143143
144 Id.Pointer => {
145 const ptr_type = @fieldParentPtr(Pointer, "base", base);
146 return ptr_type.key.child_type.hasBits();
147 },
148
144149 Id.ErrorSet => @panic("TODO"),
145150 Id.Enum => @panic("TODO"),
146 Id.Pointer => @panic("TODO"),
147151 Id.Struct => @panic("TODO"),
148152 Id.Array => @panic("TODO"),
149153 Id.Optional => @panic("TODO"),
......@@ -222,29 +226,65 @@ pub const Type = struct {
222226 pub const Fn = struct {
223227 base: Type,
224228 key: Key,
229 non_key: NonKey,
225230 garbage_node: std.atomic.Stack(*Fn).Node,
226231
232 pub const Kind = enum {
233 Normal,
234 Generic,
235 };
236
237 pub const NonKey = union {
238 Normal: Normal,
239 Generic: void,
240
241 pub const Normal = struct {
242 variable_list: std.ArrayList(*Scope.Var),
243 };
244 };
245
227246 pub const Key = struct {
228247 data: Data,
229248 alignment: ?u32,
230249
231 pub const Data = union(enum) {
250 pub const Data = union(Kind) {
232251 Generic: Generic,
233252 Normal: Normal,
234253 };
235254
255 pub const Normal = struct {
256 params: []Param,
257 return_type: *Type,
258 is_var_args: bool,
259 cc: CallingConvention,
260 };
261
262 pub const Generic = struct {
263 param_count: usize,
264 cc: CC,
265
266 pub const CC = union(CallingConvention) {
267 Auto,
268 C,
269 Cold,
270 Naked,
271 Stdcall,
272 Async: *Type, // allocator type
273 };
274 };
275
236276 pub fn hash(self: *const Key) u32 {
237277 var result: u32 = 0;
238278 result +%= hashAny(self.alignment, 0);
239279 switch (self.data) {
240 Data.Generic => |generic| {
280 Kind.Generic => |generic| {
241281 result +%= hashAny(generic.param_count, 1);
242282 switch (generic.cc) {
243283 CallingConvention.Async => |allocator_type| result +%= hashAny(allocator_type, 2),
244284 else => result +%= hashAny(CallingConvention(generic.cc), 3),
245285 }
246286 },
247 Data.Normal => |normal| {
287 Kind.Normal => |normal| {
248288 result +%= hashAny(normal.return_type, 4);
249289 result +%= hashAny(normal.is_var_args, 5);
250290 result +%= hashAny(normal.cc, 6);
......@@ -264,7 +304,7 @@ pub const Type = struct {
264304 }
265305 if (@TagType(Data)(self.data) != @TagType(Data)(other.data)) return false;
266306 switch (self.data) {
267 Data.Generic => |*self_generic| {
307 Kind.Generic => |*self_generic| {
268308 const other_generic = &other.data.Generic;
269309 if (self_generic.param_count != other_generic.param_count) return false;
270310 if (CallingConvention(self_generic.cc) != CallingConvention(other_generic.cc)) return false;
......@@ -276,7 +316,7 @@ pub const Type = struct {
276316 else => {},
277317 }
278318 },
279 Data.Normal => |*self_normal| {
319 Kind.Normal => |*self_normal| {
280320 const other_normal = &other.data.Normal;
281321 if (self_normal.cc != other_normal.cc) return false;
282322 if (self_normal.is_var_args != other_normal.is_var_args) return false;
......@@ -293,13 +333,13 @@ pub const Type = struct {
293333
294334 pub fn deref(key: Key, comp: *Compilation) void {
295335 switch (key.data) {
296 Key.Data.Generic => |generic| {
336 Kind.Generic => |generic| {
297337 switch (generic.cc) {
298338 CallingConvention.Async => |allocator_type| allocator_type.base.deref(comp),
299339 else => {},
300340 }
301341 },
302 Key.Data.Normal => |normal| {
342 Kind.Normal => |normal| {
303343 normal.return_type.base.deref(comp);
304344 for (normal.params) |param| {
305345 param.typ.base.deref(comp);
......@@ -310,13 +350,13 @@ pub const Type = struct {
310350
311351 pub fn ref(key: Key) void {
312352 switch (key.data) {
313 Key.Data.Generic => |generic| {
353 Kind.Generic => |generic| {
314354 switch (generic.cc) {
315355 CallingConvention.Async => |allocator_type| allocator_type.base.ref(),
316356 else => {},
317357 }
318358 },
319 Key.Data.Normal => |normal| {
359 Kind.Normal => |normal| {
320360 normal.return_type.base.ref();
321361 for (normal.params) |param| {
322362 param.typ.base.ref();
......@@ -326,27 +366,6 @@ pub const Type = struct {
326366 }
327367 };
328368
329 pub const Normal = struct {
330 params: []Param,
331 return_type: *Type,
332 is_var_args: bool,
333 cc: CallingConvention,
334 };
335
336 pub const Generic = struct {
337 param_count: usize,
338 cc: CC,
339
340 pub const CC = union(CallingConvention) {
341 Auto,
342 C,
343 Cold,
344 Naked,
345 Stdcall,
346 Async: *Type, // allocator type
347 };
348 };
349
350369 pub const CallingConvention = enum {
351370 Auto,
352371 C,
......@@ -374,8 +393,8 @@ pub const Type = struct {
374393
375394 pub fn paramCount(self: *Fn) usize {
376395 return switch (self.key.data) {
377 Key.Data.Generic => |generic| generic.param_count,
378 Key.Data.Normal => |normal| normal.params.len,
396 Kind.Generic => |generic| generic.param_count,
397 Kind.Normal => |normal| normal.params.len,
379398 };
380399 }
381400
......@@ -394,11 +413,13 @@ pub const Type = struct {
394413 key.ref();
395414 errdefer key.deref(comp);
396415
397 const self = try comp.gpa().create(Fn{
416 const self = try comp.gpa().createOne(Fn);
417 self.* = Fn{
398418 .base = undefined,
399419 .key = key,
420 .non_key = undefined,
400421 .garbage_node = undefined,
401 });
422 };
402423 errdefer comp.gpa().destroy(self);
403424
404425 var name_buf = try std.Buffer.initSize(comp.gpa(), 0);
......@@ -407,7 +428,8 @@ pub const Type = struct {
407428 const name_stream = &std.io.BufferOutStream.init(&name_buf).stream;
408429
409430 switch (key.data) {
410 Key.Data.Generic => |generic| {
431 Kind.Generic => |generic| {
432 self.non_key = NonKey{ .Generic = {} };
411433 switch (generic.cc) {
412434 CallingConvention.Async => |async_allocator_type| {
413435 try name_stream.print("async<{}> ", async_allocator_type.name);
......@@ -429,7 +451,10 @@ pub const Type = struct {
429451 }
430452 try name_stream.write(" var");
431453 },
432 Key.Data.Normal => |normal| {
454 Kind.Normal => |normal| {
455 self.non_key = NonKey{
456 .Normal = NonKey.Normal{ .variable_list = std.ArrayList(*Scope.Var).init(comp.gpa()) },
457 };
433458 const cc_str = ccFnTypeStr(normal.cc);
434459 try name_stream.print("{}fn(", cc_str);
435460 for (normal.params) |param, i| {
......@@ -462,6 +487,12 @@ pub const Type = struct {
462487
463488 pub fn destroy(self: *Fn, comp: *Compilation) void {
464489 self.key.deref(comp);
490 switch (self.key.data) {
491 Kind.Generic => {},
492 Kind.Normal => {
493 self.non_key.Normal.variable_list.deinit();
494 },
495 }
465496 comp.gpa().destroy(self);
466497 }
467498
src-self-hosted/value.zig+19-3
......@@ -60,7 +60,7 @@ pub const Value = struct {
6060 pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) {
6161 switch (base.id) {
6262 Id.Type => unreachable,
63 Id.Fn => @panic("TODO"),
63 Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile),
6464 Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
6565 Id.Void => return null,
6666 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
......@@ -180,7 +180,7 @@ pub const Value = struct {
180180 child_scope: *Scope,
181181
182182 /// parent is child_scope
183 block_scope: *Scope.Block,
183 block_scope: ?*Scope.Block,
184184
185185 /// Path to the object file that contains this function
186186 containing_object: Buffer,
......@@ -205,7 +205,7 @@ pub const Value = struct {
205205 },
206206 .fndef_scope = fndef_scope,
207207 .child_scope = &fndef_scope.base,
208 .block_scope = undefined,
208 .block_scope = null,
209209 .symbol_name = symbol_name,
210210 .containing_object = Buffer.initNull(comp.gpa()),
211211 .link_set_node = link_set_node,
......@@ -231,6 +231,22 @@ pub const Value = struct {
231231 self.symbol_name.deinit();
232232 comp.gpa().destroy(self);
233233 }
234
235 /// We know that the function definition will end up in an .o file somewhere.
236 /// Here, all we have to do is generate a global prototype.
237 /// TODO cache the prototype per ObjectFile
238 pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?llvm.ValueRef {
239 const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
240 const llvm_fn = llvm.AddFunction(
241 ofile.module,
242 self.symbol_name.ptr(),
243 llvm_fn_type,
244 ) orelse return error.OutOfMemory;
245
246 // TODO port more logic from codegen.cpp:fn_llvm_value
247
248 return llvm_fn;
249 }
234250 };
235251
236252 pub const Void = struct {