| ... | @@ -14,7 +14,8 @@ const text = @import("ir/text.zig"); | ... | @@ -14,7 +14,8 @@ const text = @import("ir/text.zig"); |
| 14 | pub const Inst = struct { | 14 | pub const Inst = struct { |
| 15 | tag: Tag, | 15 | tag: Tag, |
| 16 | ty: Type, | 16 | ty: Type, |
| 17 | src_offset: usize, | 17 | /// Byte offset into the source. |
| | 18 | src: usize, |
| 18 | | 19 | |
| 19 | pub const Tag = enum { | 20 | pub const Tag = enum { |
| 20 | unreach, | 21 | unreach, |
| ... | @@ -29,6 +30,15 @@ pub const Inst = struct { | ... | @@ -29,6 +30,15 @@ pub const Inst = struct { |
| 29 | return @fieldParentPtr(T, "base", base); | 30 | return @fieldParentPtr(T, "base", base); |
| 30 | } | 31 | } |
| 31 | | 32 | |
| | 33 | /// Returns `null` if runtime-known. |
| | 34 | pub fn value(base: *Inst) ?Value { |
| | 35 | return switch (base.tag) { |
| | 36 | .unreach => Value.initTag(.noreturn_value), |
| | 37 | .constant => base.cast(Constant).?.val, |
| | 38 | .assembly => null, |
| | 39 | }; |
| | 40 | } |
| | 41 | |
| 32 | pub const Constant = struct { | 42 | pub const Constant = struct { |
| 33 | pub const base_tag = Tag.constant; | 43 | pub const base_tag = Tag.constant; |
| 34 | base: Inst, | 44 | base: Inst, |
| ... | @@ -156,9 +166,7 @@ const Analyze = struct { | ... | @@ -156,9 +166,7 @@ const Analyze = struct { |
| 156 | } | 166 | } |
| 157 | | 167 | |
| 158 | fn resolveConstValue(self: *Analyze, base: *Inst) !Value { | 168 | fn resolveConstValue(self: *Analyze, base: *Inst) !Value { |
| 159 | const const_inst = base.cast(Inst.Constant) orelse | 169 | return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{}); |
| 160 | return self.fail(base.src_offset, "unable to resolve comptime value", .{}); | | |
| 161 | return const_inst.val; | | |
| 162 | } | 170 | } |
| 163 | | 171 | |
| 164 | fn resolveConstString(self: *Analyze, old_inst: *text.Inst) ![]u8 { | 172 | fn resolveConstString(self: *Analyze, old_inst: *text.Inst) ![]u8 { |
| ... | @@ -176,7 +184,7 @@ const Analyze = struct { | ... | @@ -176,7 +184,7 @@ const Analyze = struct { |
| 176 | switch (typed_value.ty.zigTypeTag()) { | 184 | switch (typed_value.ty.zigTypeTag()) { |
| 177 | .Fn => {}, | 185 | .Fn => {}, |
| 178 | else => return self.fail( | 186 | else => return self.fail( |
| 179 | export_inst.positionals.value.src_offset, | 187 | export_inst.positionals.value.src, |
| 180 | "unable to export type '{}'", | 188 | "unable to export type '{}'", |
| 181 | .{typed_value.ty}, | 189 | .{typed_value.ty}, |
| 182 | ), | 190 | ), |
| ... | @@ -187,7 +195,20 @@ const Analyze = struct { | ... | @@ -187,7 +195,20 @@ const Analyze = struct { |
| 187 | }); | 195 | }); |
| 188 | } | 196 | } |
| 189 | | 197 | |
| 190 | fn constStr(self: *Analyze, src_offset: usize, str: []const u8) !*Inst { | 198 | fn constInst(self: *Analyze, src: usize, typed_value: TypedValue) !*Inst { |
| | 199 | const const_inst = try self.arena.allocator.create(Inst.Constant); |
| | 200 | const_inst.* = .{ |
| | 201 | .base = .{ |
| | 202 | .tag = Inst.Constant.base_tag, |
| | 203 | .ty = typed_value.ty, |
| | 204 | .src = src, |
| | 205 | }, |
| | 206 | .val = typed_value.val, |
| | 207 | }; |
| | 208 | return &const_inst.base; |
| | 209 | } |
| | 210 | |
| | 211 | fn constStr(self: *Analyze, src: usize, str: []const u8) !*Inst { |
| 191 | const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0); | 212 | const array_payload = try self.arena.allocator.create(Type.Payload.Array_u8_Sentinel0); |
| 192 | array_payload.* = .{ .len = str.len }; | 213 | array_payload.* = .{ .len = str.len }; |
| 193 | | 214 | |
| ... | @@ -197,16 +218,10 @@ const Analyze = struct { | ... | @@ -197,16 +218,10 @@ const Analyze = struct { |
| 197 | const bytes_payload = try self.arena.allocator.create(Value.Payload.Bytes); | 218 | const bytes_payload = try self.arena.allocator.create(Value.Payload.Bytes); |
| 198 | bytes_payload.* = .{ .data = str }; | 219 | bytes_payload.* = .{ .data = str }; |
| 199 | | 220 | |
| 200 | const const_inst = try self.arena.allocator.create(Inst.Constant); | 221 | return self.constInst(src, .{ |
| 201 | const_inst.* = .{ | 222 | .ty = Type.initPayload(&ty_payload.base), |
| 202 | .base = .{ | | |
| 203 | .tag = Inst.Constant.base_tag, | | |
| 204 | .ty = Type.initPayload(&ty_payload.base), | | |
| 205 | .src_offset = src_offset, | | |
| 206 | }, | | |
| 207 | .val = Value.initPayload(&bytes_payload.base), | 223 | .val = Value.initPayload(&bytes_payload.base), |
| 208 | }; | 224 | }); |
| 209 | return &const_inst.base; | | |
| 210 | } | 225 | } |
| 211 | | 226 | |
| 212 | fn analyzeDecl(self: *Analyze, old_inst: *text.Inst) !*Inst { | 227 | fn analyzeDecl(self: *Analyze, old_inst: *text.Inst) !*Inst { |
| ... | @@ -215,23 +230,28 @@ const Analyze = struct { | ... | @@ -215,23 +230,28 @@ const Analyze = struct { |
| 215 | // We can use this reference because Inst.Const's Value is arena-allocated. | 230 | // We can use this reference because Inst.Const's Value is arena-allocated. |
| 216 | // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends. | 231 | // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends. |
| 217 | const bytes = old_inst.cast(text.Inst.Str).?.positionals.bytes; | 232 | const bytes = old_inst.cast(text.Inst.Str).?.positionals.bytes; |
| 218 | return self.constStr(old_inst.src_offset, bytes); | 233 | return self.constStr(old_inst.src, bytes); |
| 219 | }, | 234 | }, |
| 220 | .int => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | 235 | .int => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 221 | .ptrtoint => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | 236 | .ptrtoint => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 222 | .fieldptr => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | 237 | .fieldptr => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 223 | .deref => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | 238 | .deref => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 224 | .as => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | 239 | .as => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 225 | .@"asm" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | 240 | .@"asm" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 226 | .@"unreachable" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | 241 | .@"unreachable" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 227 | .@"fn" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | 242 | .@"fn" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 228 | .@"export" => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | 243 | .@"export" => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 229 | .primitive => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | 244 | .primitive => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 230 | .fntype => return self.fail(old_inst.src_offset, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), | 245 | .fntype => return self.fail(old_inst.src, "TODO implement analyzing {}", .{@tagName(old_inst.tag)}), |
| 231 | } | 246 | } |
| 232 | } | 247 | } |
| 233 | | 248 | |
| 234 | fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst { | 249 | fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst { |
| | 250 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); |
| | 251 | if (in_memory_result == .ok) { |
| | 252 | return self.bitcast(dest_type, inst); |
| | 253 | } |
| | 254 | |
| 235 | // *[N]T to []T | 255 | // *[N]T to []T |
| 236 | if (inst.ty.isSinglePointer() and dest_type.isSlice() and | 256 | if (inst.ty.isSinglePointer() and dest_type.isSlice() and |
| 237 | (!inst.ty.pointerIsConst() or dest_type.pointerIsConst())) | 257 | (!inst.ty.pointerIsConst() or dest_type.pointerIsConst())) |
| ... | @@ -241,17 +261,29 @@ const Analyze = struct { | ... | @@ -241,17 +261,29 @@ const Analyze = struct { |
| 241 | if (array_type.zigTypeTag() == .Array and | 261 | if (array_type.zigTypeTag() == .Array and |
| 242 | coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok) | 262 | coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok) |
| 243 | { | 263 | { |
| 244 | return self.fail(inst.src_offset, "TODO do the type coercion", .{}); | 264 | return self.coerceArrayPtrToSlice(dest_type, inst); |
| 245 | } | 265 | } |
| 246 | } | 266 | } |
| 247 | return self.fail(inst.src_offset, "TODO implement type coercion", .{}); | 267 | return self.fail(inst.src, "TODO implement type coercion", .{}); |
| | 268 | } |
| | 269 | |
| | 270 | fn bitcast(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst { |
| | 271 | return self.fail(inst.src, "TODO implement bitcast analysis", .{}); |
| | 272 | } |
| | 273 | |
| | 274 | fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst { |
| | 275 | if (inst.value()) |val| { |
| | 276 | // The comptime Value representation is compatible with both types. |
| | 277 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); |
| | 278 | } |
| | 279 | return self.fail(inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); |
| 248 | } | 280 | } |
| 249 | | 281 | |
| 250 | fn fail(self: *Analyze, src_offset: usize, comptime format: []const u8, args: var) InnerError { | 282 | fn fail(self: *Analyze, src: usize, comptime format: []const u8, args: var) InnerError { |
| 251 | @setCold(true); | 283 | @setCold(true); |
| 252 | const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args); | 284 | const msg = try std.fmt.allocPrint(&self.arena.allocator, format, args); |
| 253 | (try self.errors.addOne()).* = .{ | 285 | (try self.errors.addOne()).* = .{ |
| 254 | .byte_offset = src_offset, | 286 | .byte_offset = src, |
| 255 | .msg = msg, | 287 | .msg = msg, |
| 256 | }; | 288 | }; |
| 257 | return error.AnalysisFail; | 289 | return error.AnalysisFail; |