authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-21 16:06:15-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-21 16:06:15-04:00
log22e7ca5613c32b14043dee4531fe8b180bfc407a
tree9c802426d9ee56ac6554b0aaf010e956bd492507
parent9a2ea5ca420c7fdd4ab22b1f8814fa068d8ac56e

ir: analysis of fn instruction


3 files changed, 115 insertions(+), 20 deletions(-)

lib/std/mem.zig+1-1
...@@ -118,7 +118,7 @@ pub const Allocator = struct {...@@ -118,7 +118,7 @@ pub const Allocator = struct {
118 ptr[n] = sentinel;118 ptr[n] = sentinel;
119 return ptr[0..n :sentinel];119 return ptr[0..n :sentinel];
120 } else {120 } else {
121 return alignedAlloc(Elem, optional_alignment, n);121 return self.alignedAlloc(Elem, optional_alignment, n);
122 }122 }
123 }123 }
124124
src-self-hosted/ir.zig+89-19
...@@ -68,12 +68,18 @@ pub const Module = struct {...@@ -68,12 +68,18 @@ pub const Module = struct {
68 exports: []Export,68 exports: []Export,
69 errors: []ErrorMsg,69 errors: []ErrorMsg,
70 arena: std.heap.ArenaAllocator,70 arena: std.heap.ArenaAllocator,
71 fns: []Fn,
7172
72 pub const Export = struct {73 pub const Export = struct {
73 name: []const u8,74 name: []const u8,
74 typed_value: TypedValue,75 typed_value: TypedValue,
75 };76 };
7677
78 pub const Fn = struct {
79 analysis_status: enum { in_progress, failure, success },
80 body: []*Inst,
81 };
82
77 pub fn deinit(self: *Module, allocator: *Allocator) void {83 pub fn deinit(self: *Module, allocator: *Allocator) void {
78 allocator.free(self.exports);84 allocator.free(self.exports);
79 allocator.free(self.errors);85 allocator.free(self.errors);
...@@ -97,12 +103,14 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {...@@ -97,12 +103,14 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
97 .arena = std.heap.ArenaAllocator.init(allocator),103 .arena = std.heap.ArenaAllocator.init(allocator),
98 .old_module = &old_module,104 .old_module = &old_module,
99 .errors = std.ArrayList(ErrorMsg).init(allocator),105 .errors = std.ArrayList(ErrorMsg).init(allocator),
100 .inst_table = std.AutoHashMap(*text.Inst, Analyze.NewInst).init(allocator),106 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
101 .exports = std.ArrayList(Module.Export).init(allocator),107 .exports = std.ArrayList(Module.Export).init(allocator),
108 .fns = std.ArrayList(Module.Fn).init(allocator),
102 };109 };
103 defer ctx.errors.deinit();110 defer ctx.errors.deinit();
104 defer ctx.inst_table.deinit();111 defer ctx.decl_table.deinit();
105 defer ctx.exports.deinit();112 defer ctx.exports.deinit();
113 defer ctx.fns.deinit();
106114
107 ctx.analyzeRoot() catch |err| switch (err) {115 ctx.analyzeRoot() catch |err| switch (err) {
108 error.AnalysisFail => {116 error.AnalysisFail => {
...@@ -113,6 +121,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {...@@ -113,6 +121,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
113 return Module{121 return Module{
114 .exports = ctx.exports.toOwnedSlice(),122 .exports = ctx.exports.toOwnedSlice(),
115 .errors = ctx.errors.toOwnedSlice(),123 .errors = ctx.errors.toOwnedSlice(),
124 .fns = ctx.fns.toOwnedSlice(),
116 .arena = ctx.arena,125 .arena = ctx.arena,
117 };126 };
118}127}
...@@ -122,42 +131,57 @@ const Analyze = struct {...@@ -122,42 +131,57 @@ const Analyze = struct {
122 arena: std.heap.ArenaAllocator,131 arena: std.heap.ArenaAllocator,
123 old_module: *const text.Module,132 old_module: *const text.Module,
124 errors: std.ArrayList(ErrorMsg),133 errors: std.ArrayList(ErrorMsg),
125 inst_table: std.AutoHashMap(*text.Inst, NewInst),134 decl_table: std.AutoHashMap(*text.Inst, NewDecl),
126 exports: std.ArrayList(Module.Export),135 exports: std.ArrayList(Module.Export),
136 fns: std.ArrayList(Module.Fn),
127137
128 const NewInst = struct {138 const NewDecl = struct {
129 /// null means a semantic analysis error happened139 /// null means a semantic analysis error happened
130 ptr: ?*Inst,140 ptr: ?*Inst,
131 };141 };
132142
143 const NewInst = struct {
144 ptr: *Inst,
145 };
146
147 const Fn = struct {
148 body: std.ArrayList(*Inst),
149 inst_table: std.AutoHashMap(*text.Inst, NewInst),
150 /// Index into Module fns array
151 fn_index: usize,
152 };
153
133 const InnerError = error{ OutOfMemory, AnalysisFail };154 const InnerError = error{ OutOfMemory, AnalysisFail };
134155
135 fn analyzeRoot(self: *Analyze) !void {156 fn analyzeRoot(self: *Analyze) !void {
136 for (self.old_module.decls) |decl| {157 for (self.old_module.decls) |decl| {
137 if (decl.cast(text.Inst.Export)) |export_inst| {158 if (decl.cast(text.Inst.Export)) |export_inst| {
138 try analyzeExport(self, export_inst);159 try analyzeExport(self, null, export_inst);
139 }160 }
140 }161 }
141 }162 }
142163
143 fn resolveInst(self: *Analyze, old_inst: *text.Inst) InnerError!*Inst {164 fn resolveInst(self: *Analyze, opt_func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst {
144 if (self.inst_table.get(old_inst)) |kv| {165 if (opt_func) |func| {
166 const kv = func.inst_table.get(old_inst) orelse return error.AnalysisFail;
167 return kv.value.ptr;
168 } else if (self.decl_table.get(old_inst)) |kv| {
145 return kv.value.ptr orelse return error.AnalysisFail;169 return kv.value.ptr orelse return error.AnalysisFail;
146 } else {170 } else {
147 const new_inst = self.analyzeDecl(old_inst) catch |err| switch (err) {171 const new_inst = self.analyzeInst(old_inst, null) catch |err| switch (err) {
148 error.AnalysisFail => {172 error.AnalysisFail => {
149 try self.inst_table.putNoClobber(old_inst, .{ .ptr = null });173 try self.decl_table.putNoClobber(old_inst, .{ .ptr = null });
150 return error.AnalysisFail;174 return error.AnalysisFail;
151 },175 },
152 else => |e| return e,176 else => |e| return e,
153 };177 };
154 try self.inst_table.putNoClobber(old_inst, .{ .ptr = new_inst });178 try self.decl_table.putNoClobber(old_inst, .{ .ptr = new_inst });
155 return new_inst;179 return new_inst;
156 }180 }
157 }181 }
158182
159 fn resolveInstConst(self: *Analyze, old_inst: *text.Inst) InnerError!TypedValue {183 fn resolveInstConst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!TypedValue {
160 const new_inst = try self.resolveInst(old_inst);184 const new_inst = try self.resolveInst(func, old_inst);
161 const val = try self.resolveConstValue(new_inst);185 const val = try self.resolveConstValue(new_inst);
162 return TypedValue{186 return TypedValue{
163 .ty = new_inst.ty,187 .ty = new_inst.ty,
...@@ -169,17 +193,25 @@ const Analyze = struct {...@@ -169,17 +193,25 @@ const Analyze = struct {
169 return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{});193 return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{});
170 }194 }
171195
172 fn resolveConstString(self: *Analyze, old_inst: *text.Inst) ![]u8 {196 fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 {
173 const new_inst = try self.resolveInst(old_inst);197 const new_inst = try self.resolveInst(func, old_inst);
174 const wanted_type = Type.initTag(.const_slice_u8);198 const wanted_type = Type.initTag(.const_slice_u8);
175 const coerced_inst = try self.coerce(wanted_type, new_inst);199 const coerced_inst = try self.coerce(wanted_type, new_inst);
176 const val = try self.resolveConstValue(coerced_inst);200 const val = try self.resolveConstValue(coerced_inst);
177 return val.toAllocatedBytes(&self.arena.allocator);201 return val.toAllocatedBytes(&self.arena.allocator);
178 }202 }
179203
180 fn analyzeExport(self: *Analyze, export_inst: *text.Inst.Export) !void {204 fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type {
181 const symbol_name = try self.resolveConstString(export_inst.positionals.symbol_name);205 const new_inst = try self.resolveInst(func, old_inst);
182 const typed_value = try self.resolveInstConst(export_inst.positionals.value);206 const wanted_type = Type.initTag(.@"type");
207 const coerced_inst = try self.coerce(wanted_type, new_inst);
208 const val = try self.resolveConstValue(coerced_inst);
209 return val.toType();
210 }
211
212 fn analyzeExport(self: *Analyze, func: ?*Fn, export_inst: *text.Inst.Export) !void {
213 const symbol_name = try self.resolveConstString(func, export_inst.positionals.symbol_name);
214 const typed_value = try self.resolveInstConst(func, export_inst.positionals.value);
183215
184 switch (typed_value.ty.zigTypeTag()) {216 switch (typed_value.ty.zigTypeTag()) {
185 .Fn => {},217 .Fn => {},
...@@ -224,7 +256,7 @@ const Analyze = struct {...@@ -224,7 +256,7 @@ const Analyze = struct {
224 });256 });
225 }257 }
226258
227 fn analyzeDecl(self: *Analyze, old_inst: *text.Inst) !*Inst {259 fn analyzeInst(self: *Analyze, old_inst: *text.Inst, opt_func: ?*Fn) InnerError!*Inst {
228 switch (old_inst.tag) {260 switch (old_inst.tag) {
229 .str => {261 .str => {
230 // We can use this reference because Inst.Const's Value is arena-allocated.262 // We can use this reference because Inst.Const's Value is arena-allocated.
...@@ -239,7 +271,45 @@ const Analyze = struct {...@@ -239,7 +271,45 @@ const Analyze = struct {
239 .as => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),271 .as => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
240 .@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),272 .@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
241 .@"unreachable" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),273 .@"unreachable" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
242 .@"fn" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),274 .@"fn" => {
275 const fn_inst = old_inst.cast(text.Inst.Fn).?;
276 const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type);
277
278 var new_func: Fn = .{
279 .body = std.ArrayList(*Inst).init(self.allocator),
280 .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator),
281 .fn_index = self.fns.items.len,
282 };
283 defer new_func.body.deinit();
284 defer new_func.inst_table.deinit();
285 // Don't hang on to a reference to this when analyzing body instructions, since the memory
286 // could become invalid.
287 (try self.fns.addOne()).* = .{
288 .analysis_status = .in_progress,
289 .body = undefined,
290 };
291
292 for (fn_inst.positionals.body.instructions) |src_inst| {
293 const new_inst = self.analyzeInst(src_inst, &new_func) catch |err| {
294 self.fns.items[new_func.fn_index].analysis_status = .failure;
295 return err;
296 };
297 try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst });
298 }
299
300 self.fns.items[new_func.fn_index] = .{
301 .analysis_status = .success,
302 .body = new_func.body.toOwnedSlice(),
303 };
304
305 const fn_payload = try self.arena.allocator.create(Value.Payload.Function);
306 fn_payload.* = .{ .index = new_func.fn_index };
307
308 return self.constInst(old_inst.src, .{
309 .ty = fn_type,
310 .val = Value.initPayload(&fn_payload.base),
311 });
312 },
243 .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),313 .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
244 .primitive => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),314 .primitive => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
245 .fntype => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),315 .fntype => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}),
src-self-hosted/value.zig+25
...@@ -100,6 +100,29 @@ pub const Value = extern union {...@@ -100,6 +100,29 @@ pub const Value = extern union {
100 unreachable;100 unreachable;
101 }101 }
102102
103 /// Asserts that the value is representable as a type.
104 pub fn toType(self: Value) Type {
105 return switch (self.tag()) {
106 .ty => self.cast(Payload.Ty).?.ty,
107
108 .void_type => Type.initTag(.@"void"),
109 .noreturn_type => Type.initTag(.@"noreturn"),
110 .bool_type => Type.initTag(.@"bool"),
111 .usize_type => Type.initTag(.@"usize"),
112
113 .void_value,
114 .noreturn_value,
115 .bool_true,
116 .bool_false,
117 .int_u64,
118 .int_i64,
119 .function,
120 .ref,
121 .bytes,
122 => unreachable,
123 };
124 }
125
103 /// This type is not copyable since it may contain pointers to its inner data.126 /// This type is not copyable since it may contain pointers to its inner data.
104 pub const Payload = struct {127 pub const Payload = struct {
105 tag: Tag,128 tag: Tag,
...@@ -116,6 +139,8 @@ pub const Value = extern union {...@@ -116,6 +139,8 @@ pub const Value = extern union {
116139
117 pub const Function = struct {140 pub const Function = struct {
118 base: Payload = Payload{ .tag = .function },141 base: Payload = Payload{ .tag = .function },
142 /// Index into the `fns` array of the `ir.Module`
143 index: usize,
119 };144 };
120145
121 pub const ArraySentinel0_u8_Type = struct {146 pub const ArraySentinel0_u8_Type = struct {