2018-07-12 12:08:40 -07:00
|
|
|
const std = @import("std");
|
|
|
|
const builtin = @import("builtin");
|
|
|
|
const Scope = @import("scope.zig").Scope;
|
2018-07-14 13:12:41 -07:00
|
|
|
const Compilation = @import("compilation.zig").Compilation;
|
2018-07-14 21:04:12 -07:00
|
|
|
const ObjectFile = @import("codegen.zig").ObjectFile;
|
|
|
|
const llvm = @import("llvm.zig");
|
2018-07-16 17:52:50 -07:00
|
|
|
const Buffer = std.Buffer;
|
2018-07-19 22:46:49 -07:00
|
|
|
const assert = std.debug.assert;
|
2018-07-12 12:08:40 -07:00
|
|
|
|
|
|
|
/// Values are ref-counted, heap-allocated, and copy-on-write
|
|
|
|
/// If there is only 1 ref then write need not copy
|
|
|
|
pub const Value = struct {
|
|
|
|
id: Id,
|
|
|
|
typeof: *Type,
|
2018-07-14 12:45:15 -07:00
|
|
|
ref_count: std.atomic.Int(usize),
|
2018-07-12 12:08:40 -07:00
|
|
|
|
2018-07-14 12:45:15 -07:00
|
|
|
/// Thread-safe
|
2018-07-12 12:08:40 -07:00
|
|
|
pub fn ref(base: *Value) void {
|
2018-07-14 12:45:15 -07:00
|
|
|
_ = base.ref_count.incr();
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
|
|
|
|
2018-07-14 12:45:15 -07:00
|
|
|
/// Thread-safe
|
2018-07-14 13:12:41 -07:00
|
|
|
pub fn deref(base: *Value, comp: *Compilation) void {
|
2018-07-14 12:45:15 -07:00
|
|
|
if (base.ref_count.decr() == 1) {
|
2018-07-14 13:12:41 -07:00
|
|
|
base.typeof.base.deref(comp);
|
2018-07-12 12:08:40 -07:00
|
|
|
switch (base.id) {
|
2018-07-14 13:12:41 -07:00
|
|
|
Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp),
|
|
|
|
Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
|
|
|
|
Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),
|
|
|
|
Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
|
|
|
|
Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
|
|
|
|
Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),
|
2018-07-18 21:08:47 -07:00
|
|
|
Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp),
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-19 22:46:49 -07:00
|
|
|
pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {
|
|
|
|
base.typeof.base.deref(comp);
|
|
|
|
new_type.base.ref();
|
|
|
|
base.typeof = new_type;
|
|
|
|
}
|
|
|
|
|
2018-07-13 18:56:38 -07:00
|
|
|
pub fn getRef(base: *Value) *Value {
|
|
|
|
base.ref();
|
|
|
|
return base;
|
|
|
|
}
|
|
|
|
|
2018-07-19 12:11:39 -07:00
|
|
|
pub fn cast(base: *Value, comptime T: type) ?*T {
|
|
|
|
if (base.id != @field(Id, @typeName(T))) return null;
|
|
|
|
return @fieldParentPtr(T, "base", base);
|
|
|
|
}
|
|
|
|
|
2018-07-12 12:08:40 -07:00
|
|
|
pub fn dump(base: *const Value) void {
|
|
|
|
std.debug.warn("{}", @tagName(base.id));
|
|
|
|
}
|
|
|
|
|
2018-07-14 21:04:12 -07:00
|
|
|
pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) {
|
|
|
|
switch (base.id) {
|
|
|
|
Id.Type => unreachable,
|
|
|
|
Id.Fn => @panic("TODO"),
|
|
|
|
Id.Void => return null,
|
|
|
|
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
|
|
|
|
Id.NoReturn => unreachable,
|
|
|
|
Id.Ptr => @panic("TODO"),
|
2018-07-18 21:08:47 -07:00
|
|
|
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile),
|
2018-07-14 21:04:12 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-19 22:46:49 -07:00
|
|
|
pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
|
|
|
|
if (self.ref_count.get() == 1) {
|
|
|
|
// ( ͡° ͜ʖ ͡°)
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(self.ref_count.decr() != 1);
|
|
|
|
return self.copy(comp);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
|
|
|
|
switch (base.id) {
|
|
|
|
Id.Type => unreachable,
|
|
|
|
Id.Fn => unreachable,
|
|
|
|
Id.Void => unreachable,
|
|
|
|
Id.Bool => unreachable,
|
|
|
|
Id.NoReturn => unreachable,
|
|
|
|
Id.Ptr => unreachable,
|
|
|
|
Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-12 12:08:40 -07:00
|
|
|
pub const Id = enum {
|
|
|
|
Type,
|
|
|
|
Fn,
|
|
|
|
Void,
|
|
|
|
Bool,
|
|
|
|
NoReturn,
|
2018-07-13 18:56:38 -07:00
|
|
|
Ptr,
|
2018-07-18 21:08:47 -07:00
|
|
|
Int,
|
2018-07-12 12:08:40 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
pub const Type = @import("type.zig").Type;
|
|
|
|
|
|
|
|
pub const Fn = struct {
|
|
|
|
base: Value,
|
|
|
|
|
2018-07-14 12:45:15 -07:00
|
|
|
/// The main external name that is used in the .o file.
|
|
|
|
/// TODO https://github.com/ziglang/zig/issues/265
|
2018-07-16 17:52:50 -07:00
|
|
|
symbol_name: Buffer,
|
2018-07-14 12:45:15 -07:00
|
|
|
|
2018-07-12 12:08:40 -07:00
|
|
|
/// parent should be the top level decls or container decls
|
|
|
|
fndef_scope: *Scope.FnDef,
|
|
|
|
|
|
|
|
/// parent is scope for last parameter
|
|
|
|
child_scope: *Scope,
|
|
|
|
|
|
|
|
/// parent is child_scope
|
|
|
|
block_scope: *Scope.Block,
|
|
|
|
|
2018-07-16 17:52:50 -07:00
|
|
|
/// Path to the object file that contains this function
|
|
|
|
containing_object: Buffer,
|
|
|
|
|
|
|
|
link_set_node: *std.LinkedList(?*Value.Fn).Node,
|
|
|
|
|
2018-07-12 12:08:40 -07:00
|
|
|
/// Creates a Fn value with 1 ref
|
2018-07-14 12:45:15 -07:00
|
|
|
/// Takes ownership of symbol_name
|
2018-07-16 17:52:50 -07:00
|
|
|
pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn {
|
|
|
|
const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{
|
|
|
|
.data = null,
|
|
|
|
.next = undefined,
|
|
|
|
.prev = undefined,
|
|
|
|
});
|
|
|
|
errdefer comp.gpa().destroy(link_set_node);
|
|
|
|
|
|
|
|
const self = try comp.gpa().create(Fn{
|
2018-07-12 12:08:40 -07:00
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Fn,
|
|
|
|
.typeof = &fn_type.base,
|
2018-07-14 12:45:15 -07:00
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
2018-07-12 12:08:40 -07:00
|
|
|
},
|
|
|
|
.fndef_scope = fndef_scope,
|
|
|
|
.child_scope = &fndef_scope.base,
|
|
|
|
.block_scope = undefined,
|
2018-07-14 12:45:15 -07:00
|
|
|
.symbol_name = symbol_name,
|
2018-07-16 17:52:50 -07:00
|
|
|
.containing_object = Buffer.initNull(comp.gpa()),
|
|
|
|
.link_set_node = link_set_node,
|
2018-07-12 12:08:40 -07:00
|
|
|
});
|
|
|
|
fn_type.base.base.ref();
|
|
|
|
fndef_scope.fn_val = self;
|
|
|
|
fndef_scope.base.ref();
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
pub fn destroy(self: *Fn, comp: *Compilation) void {
|
2018-07-16 17:52:50 -07:00
|
|
|
// remove with a tombstone so that we do not have to grab a lock
|
|
|
|
if (self.link_set_node.data != null) {
|
|
|
|
// it's now the job of the link step to find this tombstone and
|
|
|
|
// deallocate it.
|
|
|
|
self.link_set_node.data = null;
|
|
|
|
} else {
|
|
|
|
comp.gpa().destroy(self.link_set_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.containing_object.deinit();
|
2018-07-14 13:12:41 -07:00
|
|
|
self.fndef_scope.base.deref(comp);
|
2018-07-14 12:45:15 -07:00
|
|
|
self.symbol_name.deinit();
|
2018-07-16 17:52:50 -07:00
|
|
|
comp.gpa().destroy(self);
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const Void = struct {
|
|
|
|
base: Value,
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
pub fn get(comp: *Compilation) *Void {
|
|
|
|
comp.void_value.base.ref();
|
|
|
|
return comp.void_value;
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
pub fn destroy(self: *Void, comp: *Compilation) void {
|
2018-07-16 17:52:50 -07:00
|
|
|
comp.gpa().destroy(self);
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const Bool = struct {
|
|
|
|
base: Value,
|
|
|
|
x: bool,
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
pub fn get(comp: *Compilation, x: bool) *Bool {
|
2018-07-12 12:08:40 -07:00
|
|
|
if (x) {
|
2018-07-14 13:12:41 -07:00
|
|
|
comp.true_value.base.ref();
|
|
|
|
return comp.true_value;
|
2018-07-12 12:08:40 -07:00
|
|
|
} else {
|
2018-07-14 13:12:41 -07:00
|
|
|
comp.false_value.base.ref();
|
|
|
|
return comp.false_value;
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
pub fn destroy(self: *Bool, comp: *Compilation) void {
|
2018-07-16 17:52:50 -07:00
|
|
|
comp.gpa().destroy(self);
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
2018-07-14 21:04:12 -07:00
|
|
|
|
|
|
|
pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef {
|
|
|
|
const llvm_type = llvm.Int1TypeInContext(ofile.context);
|
|
|
|
if (self.x) {
|
|
|
|
return llvm.ConstAllOnes(llvm_type);
|
|
|
|
} else {
|
|
|
|
return llvm.ConstNull(llvm_type);
|
|
|
|
}
|
|
|
|
}
|
2018-07-12 12:08:40 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
pub const NoReturn = struct {
|
|
|
|
base: Value,
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
pub fn get(comp: *Compilation) *NoReturn {
|
|
|
|
comp.noreturn_value.base.ref();
|
|
|
|
return comp.noreturn_value;
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
pub fn destroy(self: *NoReturn, comp: *Compilation) void {
|
2018-07-16 17:52:50 -07:00
|
|
|
comp.gpa().destroy(self);
|
2018-07-12 12:08:40 -07:00
|
|
|
}
|
|
|
|
};
|
2018-07-13 18:56:38 -07:00
|
|
|
|
|
|
|
pub const Ptr = struct {
|
|
|
|
base: Value,
|
|
|
|
|
|
|
|
pub const Mut = enum {
|
|
|
|
CompTimeConst,
|
|
|
|
CompTimeVar,
|
|
|
|
RunTime,
|
|
|
|
};
|
|
|
|
|
2018-07-14 13:12:41 -07:00
|
|
|
pub fn destroy(self: *Ptr, comp: *Compilation) void {
|
2018-07-16 17:52:50 -07:00
|
|
|
comp.gpa().destroy(self);
|
2018-07-13 18:56:38 -07:00
|
|
|
}
|
|
|
|
};
|
2018-07-18 21:08:47 -07:00
|
|
|
|
|
|
|
pub const Int = struct {
|
|
|
|
base: Value,
|
|
|
|
big_int: std.math.big.Int,
|
|
|
|
|
|
|
|
pub fn createFromString(comp: *Compilation, typeof: *Type, base: u8, value: []const u8) !*Int {
|
|
|
|
const self = try comp.gpa().create(Value.Int{
|
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Int,
|
|
|
|
.typeof = typeof,
|
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
|
|
|
},
|
|
|
|
.big_int = undefined,
|
|
|
|
});
|
|
|
|
typeof.base.ref();
|
|
|
|
errdefer comp.gpa().destroy(self);
|
|
|
|
|
|
|
|
self.big_int = try std.math.big.Int.init(comp.gpa());
|
|
|
|
errdefer self.big_int.deinit();
|
|
|
|
|
|
|
|
try self.big_int.setString(base, value);
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef {
|
|
|
|
switch (self.base.typeof.id) {
|
|
|
|
Type.Id.Int => {
|
|
|
|
const type_ref = try self.base.typeof.getLlvmType(ofile);
|
|
|
|
if (self.big_int.len == 0) {
|
|
|
|
return llvm.ConstNull(type_ref);
|
|
|
|
}
|
|
|
|
const unsigned_val = if (self.big_int.len == 1) blk: {
|
|
|
|
break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false));
|
|
|
|
} else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: {
|
|
|
|
break :blk llvm.ConstIntOfArbitraryPrecision(
|
|
|
|
type_ref,
|
|
|
|
@intCast(c_uint, self.big_int.len),
|
|
|
|
@ptrCast([*]u64, self.big_int.limbs.ptr),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
@compileError("std.math.Big.Int.Limb size does not match LLVM");
|
|
|
|
};
|
|
|
|
return if (self.big_int.positive) unsigned_val else llvm.ConstNeg(unsigned_val);
|
|
|
|
},
|
|
|
|
Type.Id.ComptimeInt => unreachable,
|
|
|
|
else => unreachable,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-19 22:46:49 -07:00
|
|
|
pub fn copy(old: *Int, comp: *Compilation) !*Int {
|
|
|
|
old.base.typeof.base.ref();
|
|
|
|
errdefer old.base.typeof.base.deref(comp);
|
|
|
|
|
|
|
|
const new = try comp.gpa().create(Value.Int{
|
|
|
|
.base = Value{
|
|
|
|
.id = Value.Id.Int,
|
|
|
|
.typeof = old.base.typeof,
|
|
|
|
.ref_count = std.atomic.Int(usize).init(1),
|
|
|
|
},
|
|
|
|
.big_int = undefined,
|
|
|
|
});
|
|
|
|
errdefer comp.gpa().destroy(new);
|
|
|
|
|
|
|
|
new.big_int = try old.big_int.clone();
|
|
|
|
errdefer new.big_int.deinit();
|
|
|
|
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
2018-07-18 21:08:47 -07:00
|
|
|
pub fn destroy(self: *Int, comp: *Compilation) void {
|
|
|
|
self.big_int.deinit();
|
|
|
|
comp.gpa().destroy(self);
|
|
|
|
}
|
|
|
|
};
|
2018-07-12 12:08:40 -07:00
|
|
|
};
|