self-hosted: function calling another function

This commit is contained in:
Andrew Kelley 2018-07-24 20:24:05 -04:00
parent 2ea08561cf
commit adefd1a52b
7 changed files with 593 additions and 171 deletions

View File

@ -6,6 +6,7 @@ const c = @import("c.zig");
const ir = @import("ir.zig");
const Value = @import("value.zig").Value;
const Type = @import("type.zig").Type;
const Scope = @import("scope.zig").Scope;
const event = std.event;
const assert = std.debug.assert;
const DW = std.dwarf;
@ -156,7 +157,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
llvm_fn_type,
) orelse return error.OutOfMemory;
const want_fn_safety = fn_val.block_scope.safety.get(ofile.comp);
const want_fn_safety = fn_val.block_scope.?.safety.get(ofile.comp);
if (want_fn_safety and ofile.comp.haveLibC()) {
try addLLVMFnAttr(ofile, llvm_fn, "sspstrong");
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)
// TODO set up error return tracing
// TODO allocate temporary stack values
// TODO create debug variable declarations for variables and allocate all local variables
const var_list = fn_type.non_key.Normal.variable_list.toSliceConst();
// create debug variable declarations for variables and allocate all local variables
for (var_list) |var_scope, i| {
const var_type = switch (var_scope.data) {
Scope.Var.Data.Const => unreachable,
Scope.Var.Data.Param => |param| param.typ,
};
// if (!type_has_bits(var->value->type)) {
// continue;
// }
// if (ir_get_var_is_comptime(var))
// continue;
// if (type_requires_comptime(var->value->type))
// continue;
// if (var->src_arg_index == SIZE_MAX) {
// var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes);
// var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
// buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1),
// var->value->type->di_type, !g->strip_debug_symbols, 0);
// } else {
// it's a parameter
// assert(var->gen_arg_index != SIZE_MAX);
// TypeTableEntry *gen_type;
// FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index];
if (var_type.handleIsPtr()) {
// if (gen_info->is_byval) {
// gen_type = var->value->type;
// } else {
// gen_type = gen_info->type;
// }
var_scope.data.Param.llvm_value = llvm.GetParam(llvm_fn, @intCast(c_uint, i));
} else {
// gen_type = var->value->type;
var_scope.data.Param.llvm_value = try renderAlloca(ofile, var_type, var_scope.name, Type.Pointer.Align.Abi);
}
// if (var->decl_node) {
// var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
// buf_ptr(&var->name), import->di_file,
// (unsigned)(var->decl_node->line + 1),
// gen_type->di_type, !g->strip_debug_symbols, 0, (unsigned)(var->gen_arg_index + 1));
// }
// }
}
// TODO finishing error return trace setup. we have to do this after all the allocas.
// TODO create debug variable declarations for parameters
// create debug variable declarations for parameters
// rely on the first variables in the variable_list being parameters.
//size_t next_var_i = 0;
for (fn_type.key.data.Normal.params) |param, i| {
//FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i];
//if (info->gen_index == SIZE_MAX)
// continue;
const scope_var = var_list[i];
//assert(variable->src_arg_index != SIZE_MAX);
//next_var_i += 1;
//assert(variable);
//assert(variable->value_ref);
if (!param.typ.handleIsPtr()) {
//clear_debug_source_node(g);
const llvm_param = llvm.GetParam(llvm_fn, @intCast(c_uint, i));
_ = renderStoreUntyped(
ofile,
llvm_param,
scope_var.data.Param.llvm_value,
Type.Pointer.Align.Abi,
Type.Pointer.Vol.Non,
);
}
//if (variable->decl_node) {
// gen_var_debug_decl(g, variable);
//}
}
for (code.basic_block_list.toSlice()) |current_block| {
llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block);
@ -294,3 +372,79 @@ fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []cons
fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void {
return addLLVMAttrInt(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val);
}
fn renderLoadUntyped(
ofile: *ObjectFile,
ptr: llvm.ValueRef,
alignment: Type.Pointer.Align,
vol: Type.Pointer.Vol,
name: [*]const u8,
) !llvm.ValueRef {
const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory;
switch (vol) {
Type.Pointer.Vol.Non => {},
Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1),
}
llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.GetElementType(llvm.TypeOf(ptr))));
return result;
}
fn renderLoad(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer, name: [*]const u8) !llvm.ValueRef {
return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name);
}
pub fn getHandleValue(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer) !?llvm.ValueRef {
const child_type = ptr_type.key.child_type;
if (!child_type.hasBits()) {
return null;
}
if (child_type.handleIsPtr()) {
return ptr;
}
return try renderLoad(ofile, ptr, ptr_type, c"");
}
pub fn renderStoreUntyped(
ofile: *ObjectFile,
value: llvm.ValueRef,
ptr: llvm.ValueRef,
alignment: Type.Pointer.Align,
vol: Type.Pointer.Vol,
) !llvm.ValueRef {
const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory;
switch (vol) {
Type.Pointer.Vol.Non => {},
Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1),
}
llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.TypeOf(value)));
return result;
}
pub fn renderStore(
ofile: *ObjectFile,
value: llvm.ValueRef,
ptr: llvm.ValueRef,
ptr_type: *Type.Pointer,
) !llvm.ValueRef {
return renderStoreUntyped(ofile, value, ptr, ptr_type.key.alignment, ptr_type.key.vol);
}
pub fn renderAlloca(
ofile: *ObjectFile,
var_type: *Type,
name: []const u8,
alignment: Type.Pointer.Align,
) !llvm.ValueRef {
const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context);
const name_with_null = try std.cstr.addNullByte(ofile.arena, name);
const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory;
llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type));
return result;
}
pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: llvm.TypeRef) u32 {
return switch (alignment) {
Type.Pointer.Align.Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type),
Type.Pointer.Align.Override => |a| a,
};
}

View File

@ -1165,49 +1165,47 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
symbol_name_consumed = true;
// Define local parameter variables
//for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
// FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
// AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
// Buf *param_name;
// bool is_var_args = param_decl_node && param_decl_node->data.param_decl.is_var_args;
// if (param_decl_node && !is_var_args) {
// param_name = param_decl_node->data.param_decl.name;
// } else {
// param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
// }
// if (param_name == nullptr) {
// continue;
// }
const root_scope = fn_decl.base.findRootScope();
for (fn_type.key.data.Normal.params) |param, i| {
//AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
const name_token = param_decl.name_token orelse {
try comp.addCompileError(root_scope, Span{
.first = param_decl.firstToken(),
.last = param_decl.type_node.firstToken(),
}, "missing parameter name");
return error.SemanticAnalysisFailed;
};
const param_name = root_scope.tree.tokenSlice(name_token);
// TypeTableEntry *param_type = param_info->type;
// bool is_noalias = param_info->is_noalias;
// if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
// add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
// }
// if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
// add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
// }
// TODO check for shadowing
// VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope,
// param_name, true, create_const_runtime(param_type), nullptr);
// var->src_arg_index = i;
// fn_table_entry->child_scope = var->child_scope;
// var->shadowable = var->shadowable || is_var_args;
const var_scope = try Scope.Var.createParam(
comp,
fn_val.child_scope,
param_name,
&param_decl.base,
i,
param.typ,
);
fn_val.child_scope = &var_scope.base;
// if (type_has_bits(param_type)) {
// fn_table_entry->variable_list.append(var);
// }
// if (fn_type->data.fn.gen_param_info) {
// var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
// }
//}
try fn_type.non_key.Normal.variable_list.append(var_scope);
}
const analyzed_code = try await (async comp.genAndAnalyzeCode(
&fndef_scope.base,
fn_val.child_scope,
body_node,
fn_type.key.data.Normal.return_type,
) catch unreachable);
errdefer analyzed_code.destroy(comp.gpa());
assert(fn_val.block_scope != null);
// Kick off rendering to LLVM module, but it doesn't block the fn decl
// analysis from being complete.
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
const key = Type.Fn.Key{
.alignment = null,
.data = Type.Fn.Key.Data{
.Normal = Type.Fn.Normal{
.Normal = Type.Fn.Key.Normal{
.return_type = return_type,
.params = params.toOwnedSlice(),
.is_var_args = false, // TODO

View File

@ -10,8 +10,10 @@ const assert = std.debug.assert;
const Token = std.zig.Token;
const Span = @import("errmsg.zig").Span;
const llvm = @import("llvm.zig");
const ObjectFile = @import("codegen.zig").ObjectFile;
const codegen = @import("codegen.zig");
const ObjectFile = codegen.ObjectFile;
const Decl = @import("decl.zig").Decl;
const mem = std.mem;
pub const LVal = enum {
None,
@ -122,6 +124,8 @@ pub const Inst = struct {
Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira),
Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira),
Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable),
Id.VarPtr => return await (async @fieldParentPtr(VarPtr, "base", base).analyze(ira) catch unreachable),
Id.LoadPtr => return await (async @fieldParentPtr(LoadPtr, "base", base).analyze(ira) catch unreachable),
}
}
@ -130,6 +134,8 @@ pub const Inst = struct {
Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),
Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val),
Id.VarPtr => return @fieldParentPtr(VarPtr, "base", base).render(ofile, fn_val),
Id.LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).render(ofile, fn_val),
Id.DeclRef => unreachable,
Id.PtrType => unreachable,
Id.Ref => @panic("TODO"),
@ -248,6 +254,8 @@ pub const Inst = struct {
Call,
DeclRef,
PtrType,
VarPtr,
LoadPtr,
};
pub const Call = struct {
@ -491,6 +499,133 @@ pub const Inst = struct {
}
};
pub const VarPtr = struct {
base: Inst,
params: Params,
const Params = struct {
var_scope: *Scope.Var,
};
const ir_val_init = IrVal.Init.Unknown;
pub fn dump(inst: *const VarPtr) void {
std.debug.warn("{}", inst.params.var_scope.name);
}
pub fn hasSideEffects(inst: *const VarPtr) bool {
return false;
}
pub async fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {
switch (self.params.var_scope.data) {
Scope.Var.Data.Const => @panic("TODO"),
Scope.Var.Data.Param => |param| {
const new_inst = try ira.irb.build(
Inst.VarPtr,
self.base.scope,
self.base.span,
Inst.VarPtr.Params{ .var_scope = self.params.var_scope },
);
const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
.child_type = param.typ,
.mut = Type.Pointer.Mut.Const,
.vol = Type.Pointer.Vol.Non,
.size = Type.Pointer.Size.One,
.alignment = Type.Pointer.Align.Abi,
}) catch unreachable);
new_inst.val = IrVal{ .KnownType = &ptr_type.base };
return new_inst;
},
}
}
pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) llvm.ValueRef {
switch (self.params.var_scope.data) {
Scope.Var.Data.Const => unreachable, // turned into Inst.Const in analyze pass
Scope.Var.Data.Param => |param| return param.llvm_value,
}
}
};
pub const LoadPtr = struct {
base: Inst,
params: Params,
const Params = struct {
target: *Inst,
};
const ir_val_init = IrVal.Init.Unknown;
pub fn dump(inst: *const LoadPtr) void {}
pub fn hasSideEffects(inst: *const LoadPtr) bool {
return false;
}
pub async fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst {
const target = try self.params.target.getAsParam();
const target_type = target.getKnownType();
if (target_type.id != Type.Id.Pointer) {
try ira.addCompileError(self.base.span, "dereference of non pointer type '{}'", target_type.name);
return error.SemanticAnalysisFailed;
}
const ptr_type = @fieldParentPtr(Type.Pointer, "base", target_type);
// if (instr_is_comptime(ptr)) {
// if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst ||
// ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
// {
// ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &ptr->value);
// if (pointee->special != ConstValSpecialRuntime) {
// IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,
// source_instruction->source_node, child_type);
// copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst);
// result->value.type = child_type;
// return result;
// }
// }
// }
const new_inst = try ira.irb.build(
Inst.LoadPtr,
self.base.scope,
self.base.span,
Inst.LoadPtr.Params{ .target = target },
);
new_inst.val = IrVal{ .KnownType = ptr_type.key.child_type };
return new_inst;
}
pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
const child_type = self.base.getKnownType();
if (!child_type.hasBits()) {
return null;
}
const ptr = self.params.target.llvm_value.?;
const ptr_type = self.params.target.getKnownType().cast(Type.Pointer).?;
return try codegen.getHandleValue(ofile, ptr, ptr_type);
//uint32_t unaligned_bit_count = ptr_type->data.pointer.unaligned_bit_count;
//if (unaligned_bit_count == 0)
// return get_handle_value(g, ptr, child_type, ptr_type);
//bool big_endian = g->is_big_endian;
//assert(!handle_is_ptr(child_type));
//LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, "");
//uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
//uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
//uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset;
//LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
//LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, "");
//return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
}
};
pub const PtrType = struct {
base: Inst,
params: Params,
@ -1160,6 +1295,7 @@ pub const Builder = struct {
Scope.Id.Block,
Scope.Id.Defer,
Scope.Id.DeferExpr,
Scope.Id.Var,
=> scope = scope.parent.?,
}
}
@ -1261,8 +1397,8 @@ pub const Builder = struct {
var child_scope = outer_block_scope;
if (parent_scope.findFnDef()) |fndef_scope| {
if (fndef_scope.fn_val.child_scope == parent_scope) {
fndef_scope.fn_val.block_scope = block_scope;
if (fndef_scope.fn_val.?.block_scope == null) {
fndef_scope.fn_val.?.block_scope = block_scope;
}
}
@ -1492,20 +1628,23 @@ pub const Builder = struct {
error.OutOfMemory => return error.OutOfMemory,
}
//VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);
//if (var) {
// IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var);
// if (lval == LValPtr)
// return var_ptr;
// else
// return ir_build_load_ptr(irb, scope, node, var_ptr);
//}
if (await (async irb.findDecl(scope, name) catch unreachable)) |decl| {
return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
.decl = decl,
.lval = lval,
});
switch (await (async irb.findIdent(scope, name) catch unreachable)) {
Ident.Decl => |decl| {
return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
.decl = decl,
.lval = lval,
});
},
Ident.VarScope => |var_scope| {
const var_ptr = try irb.build(Inst.VarPtr, scope, src_span, Inst.VarPtr.Params{ .var_scope = var_scope });
switch (lval) {
LVal.Ptr => return var_ptr,
LVal.None => {
return irb.build(Inst.LoadPtr, scope, src_span, Inst.LoadPtr.Params{ .target = var_ptr });
},
}
},
Ident.NotFound => {},
}
//if (node->owner->any_imports_failed) {
@ -1546,6 +1685,7 @@ pub const Builder = struct {
Scope.Id.Block,
Scope.Id.Decls,
Scope.Id.Root,
Scope.Id.Var,
=> scope = scope.parent orelse break,
Scope.Id.DeferExpr => unreachable,
@ -1596,6 +1736,7 @@ pub const Builder = struct {
Scope.Id.CompTime,
Scope.Id.Block,
Scope.Id.Var,
=> scope = scope.parent orelse return is_noreturn,
Scope.Id.DeferExpr => unreachable,
@ -1674,8 +1815,10 @@ pub const Builder = struct {
Type.Pointer.Size,
LVal,
*Decl,
*Scope.Var,
=> {},
// it's ok to add more types here, just make sure any instructions are ref'd appropriately
// it's ok to add more types here, just make sure that
// any instructions and basic blocks are ref'd appropriately
else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)),
}
}
@ -1773,18 +1916,30 @@ pub const Builder = struct {
//// the above blocks are rendered by ir_gen after the rest of codegen
}
async fn findDecl(irb: *Builder, scope: *Scope, name: []const u8) ?*Decl {
const Ident = union(enum) {
NotFound,
Decl: *Decl,
VarScope: *Scope.Var,
};
async fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident {
var s = scope;
while (true) {
switch (s.id) {
Scope.Id.Root => return Ident.NotFound,
Scope.Id.Decls => {
const decls = @fieldParentPtr(Scope.Decls, "base", s);
const table = await (async decls.getTableReadOnly() catch unreachable);
if (table.get(name)) |entry| {
return entry.value;
return Ident{ .Decl = entry.value };
}
},
Scope.Id.Var => {
const var_scope = @fieldParentPtr(Scope.Var, "base", s);
if (mem.eql(u8, var_scope.name, name)) {
return Ident{ .VarScope = var_scope };
}
},
Scope.Id.Root => return null,
else => {},
}
s = s.parent.?;

View File

@ -30,6 +30,7 @@ pub const AddGlobal = c.LLVMAddGlobal;
pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
pub const ArrayType = c.LLVMArrayType;
pub const BuildLoad = c.LLVMBuildLoad;
pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
pub const ConstAllOnes = c.LLVMConstAllOnes;
pub const ConstArray = c.LLVMConstArray;
@ -95,13 +96,25 @@ pub const SetInitializer = c.LLVMSetInitializer;
pub const SetLinkage = c.LLVMSetLinkage;
pub const SetTarget = c.LLVMSetTarget;
pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
pub const SetVolatile = c.LLVMSetVolatile;
pub const StructTypeInContext = c.LLVMStructTypeInContext;
pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
pub const TypeOf = c.LLVMTypeOf;
pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
pub const GetElementType = LLVMGetElementType;
extern fn LLVMGetElementType(Ty: TypeRef) TypeRef;
pub const TypeOf = LLVMTypeOf;
extern fn LLVMTypeOf(Val: ValueRef) TypeRef;
pub const BuildStore = LLVMBuildStore;
extern fn LLVMBuildStore(arg0: BuilderRef, Val: ValueRef, Ptr: ValueRef) ?ValueRef;
pub const BuildAlloca = LLVMBuildAlloca;
extern fn LLVMBuildAlloca(arg0: BuilderRef, Ty: TypeRef, Name: ?[*]const u8) ?ValueRef;
pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;

View File

@ -6,23 +6,26 @@ const Compilation = @import("compilation.zig").Compilation;
const mem = std.mem;
const ast = std.zig.ast;
const Value = @import("value.zig").Value;
const Type = @import("type.zig").Type;
const ir = @import("ir.zig");
const Span = @import("errmsg.zig").Span;
const assert = std.debug.assert;
const event = std.event;
const llvm = @import("llvm.zig");
pub const Scope = struct {
id: Id,
parent: ?*Scope,
ref_count: usize,
ref_count: std.atomic.Int(usize),
/// Thread-safe
pub fn ref(base: *Scope) void {
base.ref_count += 1;
_ = base.ref_count.incr();
}
/// Thread-safe
pub fn deref(base: *Scope, comp: *Compilation) void {
base.ref_count -= 1;
if (base.ref_count == 0) {
if (base.ref_count.decr() == 1) {
if (base.parent) |parent| parent.deref(comp);
switch (base.id) {
Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp),
@ -32,6 +35,7 @@ pub const Scope = struct {
Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),
}
}
}
@ -49,15 +53,15 @@ pub const Scope = struct {
var scope = base;
while (true) {
switch (scope.id) {
Id.FnDef => return @fieldParentPtr(FnDef, "base", base),
Id.Decls => return null,
Id.FnDef => return @fieldParentPtr(FnDef, "base", scope),
Id.Root, Id.Decls => return null,
Id.Block,
Id.Defer,
Id.DeferExpr,
Id.CompTime,
Id.Root,
=> scope = scope.parent orelse return null,
Id.Var,
=> scope = scope.parent.?,
}
}
}
@ -66,7 +70,7 @@ pub const Scope = struct {
var scope = base;
while (true) {
switch (scope.id) {
Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base),
Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope),
Id.FnDef,
Id.Decls,
@ -76,11 +80,21 @@ pub const Scope = struct {
Id.Defer,
Id.CompTime,
Id.Root,
Id.Var,
=> scope = scope.parent orelse return null,
}
}
}
fn init(base: *Scope, id: Id, parent: *Scope) void {
base.* = Scope{
.id = id,
.parent = parent,
.ref_count = std.atomic.Int(usize).init(1),
};
parent.ref();
}
pub const Id = enum {
Root,
Decls,
@ -89,6 +103,7 @@ pub const Scope = struct {
CompTime,
Defer,
DeferExpr,
Var,
};
pub const Root = struct {
@ -100,16 +115,16 @@ pub const Scope = struct {
/// Takes ownership of realpath
/// Takes ownership of tree, will deinit and destroy when done.
pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
const self = try comp.gpa().create(Root{
const self = try comp.gpa().createOne(Root);
self.* = Root{
.base = Scope{
.id = Id.Root,
.parent = null,
.ref_count = 1,
.ref_count = std.atomic.Int(usize).init(1),
},
.tree = tree,
.realpath = realpath,
});
errdefer comp.gpa().destroy(self);
};
return self;
}
@ -137,16 +152,13 @@ pub const Scope = struct {
/// Creates a Decls scope with 1 reference
pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
const self = try comp.gpa().create(Decls{
.base = Scope{
.id = Id.Decls,
.parent = parent,
.ref_count = 1,
},
const self = try comp.gpa().createOne(Decls);
self.* = Decls{
.base = undefined,
.table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
.name_future = event.Future(void).init(comp.loop),
});
parent.ref();
};
self.base.init(Id.Decls, parent);
return self;
}
@ -199,21 +211,16 @@ pub const Scope = struct {
/// Creates a Block scope with 1 reference
pub fn create(comp: *Compilation, parent: *Scope) !*Block {
const self = try comp.gpa().create(Block{
.base = Scope{
.id = Id.Block,
.parent = parent,
.ref_count = 1,
},
const self = try comp.gpa().createOne(Block);
self.* = Block{
.base = undefined,
.incoming_values = undefined,
.incoming_blocks = undefined,
.end_block = undefined,
.is_comptime = undefined,
.safety = Safety.Auto,
});
errdefer comp.gpa().destroy(self);
parent.ref();
};
self.base.init(Id.Block, parent);
return self;
}
@ -226,22 +233,17 @@ pub const Scope = struct {
base: Scope,
/// This reference is not counted so that the scope can get destroyed with the function
fn_val: *Value.Fn,
fn_val: ?*Value.Fn,
/// Creates a FnDef scope with 1 reference
/// Must set the fn_val later
pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
const self = try comp.gpa().create(FnDef{
.base = Scope{
.id = Id.FnDef,
.parent = parent,
.ref_count = 1,
},
.fn_val = undefined,
});
parent.ref();
const self = try comp.gpa().createOne(FnDef);
self.* = FnDef{
.base = undefined,
.fn_val = null,
};
self.base.init(Id.FnDef, parent);
return self;
}
@ -255,15 +257,9 @@ pub const Scope = struct {
/// Creates a CompTime scope with 1 reference
pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
const self = try comp.gpa().create(CompTime{
.base = Scope{
.id = Id.CompTime,
.parent = parent,
.ref_count = 1,
},
});
parent.ref();
const self = try comp.gpa().createOne(CompTime);
self.* = CompTime{ .base = undefined };
self.base.init(Id.CompTime, parent);
return self;
}
@ -289,20 +285,14 @@ pub const Scope = struct {
kind: Kind,
defer_expr_scope: *DeferExpr,
) !*Defer {
const self = try comp.gpa().create(Defer{
.base = Scope{
.id = Id.Defer,
.parent = parent,
.ref_count = 1,
},
const self = try comp.gpa().createOne(Defer);
self.* = Defer{
.base = undefined,
.defer_expr_scope = defer_expr_scope,
.kind = kind,
});
errdefer comp.gpa().destroy(self);
};
self.base.init(Id.Defer, parent);
defer_expr_scope.base.ref();
parent.ref();
return self;
}
@ -319,18 +309,13 @@ pub const Scope = struct {
/// Creates a DeferExpr scope with 1 reference
pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
const self = try comp.gpa().create(DeferExpr{
.base = Scope{
.id = Id.DeferExpr,
.parent = parent,
.ref_count = 1,
},
const self = try comp.gpa().createOne(DeferExpr);
self.* = DeferExpr{
.base = undefined,
.expr_node = expr_node,
.reported_err = false,
});
errdefer comp.gpa().destroy(self);
parent.ref();
};
self.base.init(Id.DeferExpr, parent);
return self;
}
@ -338,4 +323,74 @@ pub const Scope = struct {
comp.gpa().destroy(self);
}
};
pub const Var = struct {
base: Scope,
name: []const u8,
src_node: *ast.Node,
data: Data,
pub const Data = union(enum) {
Param: Param,
Const: *Value,
};
pub const Param = struct {
index: usize,
typ: *Type,
llvm_value: llvm.ValueRef,
};
pub fn createParam(
comp: *Compilation,
parent: *Scope,
name: []const u8,
src_node: *ast.Node,
param_index: usize,
param_type: *Type,
) !*Var {
const self = try create(comp, parent, name, src_node);
self.data = Data{
.Param = Param{
.index = param_index,
.typ = param_type,
.llvm_value = undefined,
},
};
return self;
}
pub fn createConst(
comp: *Compilation,
parent: *Scope,
name: []const u8,
src_node: *ast.Node,
value: *Value,
) !*Var {
const self = try create(comp, parent, name, src_node);
self.data = Data{ .Const = value };
value.ref();
return self;
}
fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var {
const self = try comp.gpa().createOne(Var);
self.* = Var{
.base = undefined,
.name = name,
.src_node = src_node,
.data = undefined,
};
self.base.init(Id.Var, parent);
return self;
}
pub fn destroy(self: *Var, comp: *Compilation) void {
switch (self.data) {
Data.Param => {},
Data.Const => |value| value.deref(comp),
}
comp.gpa().destroy(self);
}
};
};

View File

@ -141,9 +141,13 @@ pub const Type = struct {
Id.Promise,
=> return true,
Id.Pointer => {
const ptr_type = @fieldParentPtr(Pointer, "base", base);
return ptr_type.key.child_type.hasBits();
},
Id.ErrorSet => @panic("TODO"),
Id.Enum => @panic("TODO"),
Id.Pointer => @panic("TODO"),
Id.Struct => @panic("TODO"),
Id.Array => @panic("TODO"),
Id.Optional => @panic("TODO"),
@ -222,29 +226,65 @@ pub const Type = struct {
pub const Fn = struct {
base: Type,
key: Key,
non_key: NonKey,
garbage_node: std.atomic.Stack(*Fn).Node,
pub const Kind = enum {
Normal,
Generic,
};
pub const NonKey = union {
Normal: Normal,
Generic: void,
pub const Normal = struct {
variable_list: std.ArrayList(*Scope.Var),
};
};
pub const Key = struct {
data: Data,
alignment: ?u32,
pub const Data = union(enum) {
pub const Data = union(Kind) {
Generic: Generic,
Normal: Normal,
};
pub const Normal = struct {
params: []Param,
return_type: *Type,
is_var_args: bool,
cc: CallingConvention,
};
pub const Generic = struct {
param_count: usize,
cc: CC,
pub const CC = union(CallingConvention) {
Auto,
C,
Cold,
Naked,
Stdcall,
Async: *Type, // allocator type
};
};
pub fn hash(self: *const Key) u32 {
var result: u32 = 0;
result +%= hashAny(self.alignment, 0);
switch (self.data) {
Data.Generic => |generic| {
Kind.Generic => |generic| {
result +%= hashAny(generic.param_count, 1);
switch (generic.cc) {
CallingConvention.Async => |allocator_type| result +%= hashAny(allocator_type, 2),
else => result +%= hashAny(CallingConvention(generic.cc), 3),
}
},
Data.Normal => |normal| {
Kind.Normal => |normal| {
result +%= hashAny(normal.return_type, 4);
result +%= hashAny(normal.is_var_args, 5);
result +%= hashAny(normal.cc, 6);
@ -264,7 +304,7 @@ pub const Type = struct {
}
if (@TagType(Data)(self.data) != @TagType(Data)(other.data)) return false;
switch (self.data) {
Data.Generic => |*self_generic| {
Kind.Generic => |*self_generic| {
const other_generic = &other.data.Generic;
if (self_generic.param_count != other_generic.param_count) return false;
if (CallingConvention(self_generic.cc) != CallingConvention(other_generic.cc)) return false;
@ -276,7 +316,7 @@ pub const Type = struct {
else => {},
}
},
Data.Normal => |*self_normal| {
Kind.Normal => |*self_normal| {
const other_normal = &other.data.Normal;
if (self_normal.cc != other_normal.cc) return false;
if (self_normal.is_var_args != other_normal.is_var_args) return false;
@ -293,13 +333,13 @@ pub const Type = struct {
pub fn deref(key: Key, comp: *Compilation) void {
switch (key.data) {
Key.Data.Generic => |generic| {
Kind.Generic => |generic| {
switch (generic.cc) {
CallingConvention.Async => |allocator_type| allocator_type.base.deref(comp),
else => {},
}
},
Key.Data.Normal => |normal| {
Kind.Normal => |normal| {
normal.return_type.base.deref(comp);
for (normal.params) |param| {
param.typ.base.deref(comp);
@ -310,13 +350,13 @@ pub const Type = struct {
pub fn ref(key: Key) void {
switch (key.data) {
Key.Data.Generic => |generic| {
Kind.Generic => |generic| {
switch (generic.cc) {
CallingConvention.Async => |allocator_type| allocator_type.base.ref(),
else => {},
}
},
Key.Data.Normal => |normal| {
Kind.Normal => |normal| {
normal.return_type.base.ref();
for (normal.params) |param| {
param.typ.base.ref();
@ -326,27 +366,6 @@ pub const Type = struct {
}
};
pub const Normal = struct {
params: []Param,
return_type: *Type,
is_var_args: bool,
cc: CallingConvention,
};
pub const Generic = struct {
param_count: usize,
cc: CC,
pub const CC = union(CallingConvention) {
Auto,
C,
Cold,
Naked,
Stdcall,
Async: *Type, // allocator type
};
};
pub const CallingConvention = enum {
Auto,
C,
@ -374,8 +393,8 @@ pub const Type = struct {
pub fn paramCount(self: *Fn) usize {
return switch (self.key.data) {
Key.Data.Generic => |generic| generic.param_count,
Key.Data.Normal => |normal| normal.params.len,
Kind.Generic => |generic| generic.param_count,
Kind.Normal => |normal| normal.params.len,
};
}
@ -394,11 +413,13 @@ pub const Type = struct {
key.ref();
errdefer key.deref(comp);
const self = try comp.gpa().create(Fn{
const self = try comp.gpa().createOne(Fn);
self.* = Fn{
.base = undefined,
.key = key,
.non_key = undefined,
.garbage_node = undefined,
});
};
errdefer comp.gpa().destroy(self);
var name_buf = try std.Buffer.initSize(comp.gpa(), 0);
@ -407,7 +428,8 @@ pub const Type = struct {
const name_stream = &std.io.BufferOutStream.init(&name_buf).stream;
switch (key.data) {
Key.Data.Generic => |generic| {
Kind.Generic => |generic| {
self.non_key = NonKey{ .Generic = {} };
switch (generic.cc) {
CallingConvention.Async => |async_allocator_type| {
try name_stream.print("async<{}> ", async_allocator_type.name);
@ -429,7 +451,10 @@ pub const Type = struct {
}
try name_stream.write(" var");
},
Key.Data.Normal => |normal| {
Kind.Normal => |normal| {
self.non_key = NonKey{
.Normal = NonKey.Normal{ .variable_list = std.ArrayList(*Scope.Var).init(comp.gpa()) },
};
const cc_str = ccFnTypeStr(normal.cc);
try name_stream.print("{}fn(", cc_str);
for (normal.params) |param, i| {
@ -462,6 +487,12 @@ pub const Type = struct {
pub fn destroy(self: *Fn, comp: *Compilation) void {
self.key.deref(comp);
switch (self.key.data) {
Kind.Generic => {},
Kind.Normal => {
self.non_key.Normal.variable_list.deinit();
},
}
comp.gpa().destroy(self);
}

View File

@ -60,7 +60,7 @@ pub const Value = struct {
pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) {
switch (base.id) {
Id.Type => unreachable,
Id.Fn => @panic("TODO"),
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile),
Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
Id.Void => return null,
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
@ -180,7 +180,7 @@ pub const Value = struct {
child_scope: *Scope,
/// parent is child_scope
block_scope: *Scope.Block,
block_scope: ?*Scope.Block,
/// Path to the object file that contains this function
containing_object: Buffer,
@ -205,7 +205,7 @@ pub const Value = struct {
},
.fndef_scope = fndef_scope,
.child_scope = &fndef_scope.base,
.block_scope = undefined,
.block_scope = null,
.symbol_name = symbol_name,
.containing_object = Buffer.initNull(comp.gpa()),
.link_set_node = link_set_node,
@ -231,6 +231,22 @@ pub const Value = struct {
self.symbol_name.deinit();
comp.gpa().destroy(self);
}
/// We know that the function definition will end up in an .o file somewhere.
/// Here, all we have to do is generate a global prototype.
/// TODO cache the prototype per ObjectFile
pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?llvm.ValueRef {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
self.symbol_name.ptr(),
llvm_fn_type,
) orelse return error.OutOfMemory;
// TODO port more logic from codegen.cpp:fn_llvm_value
return llvm_fn;
}
};
pub const Void = struct {