| author | |
| committer | |
| log | 03adafd8023691af6a1e3d784a6e7e1f77d46859 |
| tree | 5add5d1c023d3973996c58406d44f701249be8ff |
| parent | c550eb3e8ab7ae77e5533313c219b2015e633081 |
| parent | a67d3785435ad6cbd57fca2e9247ad5ff3edfa7e |
| signature |
Compilation: forward clang diagnostics to error bundles3 files changed, 883 insertions(+), 65 deletions(-)
src/Compilation.zig+325-64| ... | @@ -77,7 +77,7 @@ embed_file_work_queue: std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic), | ... | @@ -77,7 +77,7 @@ embed_file_work_queue: std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic), |
| 77 | 77 | ||
| 78 | /// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator. | 78 | /// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator. |
| 79 | /// This data is accessed by multiple threads and is protected by `mutex`. | 79 | /// This data is accessed by multiple threads and is protected by `mutex`. |
| 80 | failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.ErrorMsg) = .{}, | 80 | failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.Diag.Bundle) = .{}, |
| 81 | 81 | ||
| 82 | /// The ErrorBundle memory is owned by the `Win32Resource`, using Compilation's general purpose allocator. | 82 | /// The ErrorBundle memory is owned by the `Win32Resource`, using Compilation's general purpose allocator. |
| 83 | /// This data is accessed by multiple threads and is protected by `mutex`. | 83 | /// This data is accessed by multiple threads and is protected by `mutex`. |
| ... | @@ -318,15 +318,286 @@ pub const CObject = struct { | ... | @@ -318,15 +318,286 @@ pub const CObject = struct { |
| 318 | failure_retryable, | 318 | failure_retryable, |
| 319 | }, | 319 | }, |
| 320 | 320 | ||
| 321 | pub const ErrorMsg = struct { | 321 | pub const Diag = struct { |
| 322 | msg: []const u8, | 322 | level: u32 = 0, |
| 323 | line: u32, | 323 | category: u32 = 0, |
| 324 | column: u32, | 324 | msg: []const u8 = &.{}, |
| 325 | src_loc: SrcLoc = .{}, | ||
| 326 | src_ranges: []const SrcRange = &.{}, | ||
| 327 | sub_diags: []const Diag = &.{}, | ||
| 328 | |||
| 329 | pub const SrcLoc = struct { | ||
| 330 | file: u32 = 0, | ||
| 331 | line: u32 = 0, | ||
| 332 | column: u32 = 0, | ||
| 333 | offset: u32 = 0, | ||
| 334 | }; | ||
| 335 | |||
| 336 | pub const SrcRange = struct { | ||
| 337 | start: SrcLoc = .{}, | ||
| 338 | end: SrcLoc = .{}, | ||
| 339 | }; | ||
| 340 | |||
| 341 | pub fn deinit(diag: *Diag, gpa: Allocator) void { | ||
| 342 | gpa.free(diag.msg); | ||
| 343 | gpa.free(diag.src_ranges); | ||
| 344 | for (diag.sub_diags) |sub_diag| { | ||
| 345 | var sub_diag_mut = sub_diag; | ||
| 346 | sub_diag_mut.deinit(gpa); | ||
| 347 | } | ||
| 348 | gpa.free(diag.sub_diags); | ||
| 349 | diag.* = undefined; | ||
| 350 | } | ||
| 325 | 351 | ||
| 326 | pub fn destroy(em: *ErrorMsg, gpa: Allocator) void { | 352 | pub fn count(diag: Diag) u32 { |
| 327 | gpa.free(em.msg); | 353 | var total: u32 = 1; |
| 328 | gpa.destroy(em); | 354 | for (diag.sub_diags) |sub_diag| total += sub_diag.count(); |
| 355 | return total; | ||
| 329 | } | 356 | } |
| 357 | |||
| 358 | pub fn addToErrorBundle(diag: Diag, eb: *ErrorBundle.Wip, bundle: Bundle, note: *u32) !void { | ||
| 359 | const err_msg = try eb.addErrorMessage(try diag.toErrorMessage(eb, bundle, 0)); | ||
| 360 | eb.extra.items[note.*] = @intFromEnum(err_msg); | ||
| 361 | note.* += 1; | ||
| 362 | for (diag.sub_diags) |sub_diag| try sub_diag.addToErrorBundle(eb, bundle, note); | ||
| 363 | } | ||
| 364 | |||
| 365 | pub fn toErrorMessage( | ||
| 366 | diag: Diag, | ||
| 367 | eb: *ErrorBundle.Wip, | ||
| 368 | bundle: Bundle, | ||
| 369 | notes_len: u32, | ||
| 370 | ) !ErrorBundle.ErrorMessage { | ||
| 371 | var start = diag.src_loc.offset; | ||
| 372 | var end = diag.src_loc.offset; | ||
| 373 | for (diag.src_ranges) |src_range| { | ||
| 374 | if (src_range.start.file == diag.src_loc.file and | ||
| 375 | src_range.start.line == diag.src_loc.line) | ||
| 376 | { | ||
| 377 | start = @min(src_range.start.offset, start); | ||
| 378 | } | ||
| 379 | if (src_range.end.file == diag.src_loc.file and | ||
| 380 | src_range.end.line == diag.src_loc.line) | ||
| 381 | { | ||
| 382 | end = @max(src_range.end.offset, end); | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | const file_name = bundle.file_names.get(diag.src_loc.file) orelse ""; | ||
| 387 | const source_line = source_line: { | ||
| 388 | if (diag.src_loc.offset == 0 or diag.src_loc.column == 0) break :source_line 0; | ||
| 389 | |||
| 390 | const file = std.fs.cwd().openFile(file_name, .{}) catch break :source_line 0; | ||
| 391 | defer file.close(); | ||
| 392 | file.seekTo(diag.src_loc.offset + 1 - diag.src_loc.column) catch break :source_line 0; | ||
| 393 | |||
| 394 | var line = std.ArrayList(u8).init(eb.gpa); | ||
| 395 | defer line.deinit(); | ||
| 396 | file.reader().readUntilDelimiterArrayList(&line, '\n', 1 << 10) catch break :source_line 0; | ||
| 397 | |||
| 398 | break :source_line try eb.addString(line.items); | ||
| 399 | }; | ||
| 400 | |||
| 401 | return .{ | ||
| 402 | .msg = try eb.addString(diag.msg), | ||
| 403 | .src_loc = try eb.addSourceLocation(.{ | ||
| 404 | .src_path = try eb.addString(file_name), | ||
| 405 | .line = diag.src_loc.line -| 1, | ||
| 406 | .column = diag.src_loc.column -| 1, | ||
| 407 | .span_start = start, | ||
| 408 | .span_main = diag.src_loc.offset, | ||
| 409 | .span_end = end + 1, | ||
| 410 | .source_line = source_line, | ||
| 411 | }), | ||
| 412 | .notes_len = notes_len, | ||
| 413 | }; | ||
| 414 | } | ||
| 415 | |||
| 416 | pub const Bundle = struct { | ||
| 417 | file_names: std.AutoHashMapUnmanaged(u32, []const u8) = .{}, | ||
| 418 | category_names: std.AutoHashMapUnmanaged(u32, []const u8) = .{}, | ||
| 419 | diags: []Diag = &.{}, | ||
| 420 | |||
| 421 | pub fn destroy(bundle: *Bundle, gpa: Allocator) void { | ||
| 422 | var file_name_it = bundle.file_names.valueIterator(); | ||
| 423 | while (file_name_it.next()) |file_name| gpa.free(file_name.*); | ||
| 424 | bundle.file_names.deinit(gpa); | ||
| 425 | |||
| 426 | var category_name_it = bundle.category_names.valueIterator(); | ||
| 427 | while (category_name_it.next()) |category_name| gpa.free(category_name.*); | ||
| 428 | bundle.category_names.deinit(gpa); | ||
| 429 | |||
| 430 | for (bundle.diags) |*diag| diag.deinit(gpa); | ||
| 431 | gpa.free(bundle.diags); | ||
| 432 | |||
| 433 | gpa.destroy(bundle); | ||
| 434 | } | ||
| 435 | |||
| 436 | pub fn parse(gpa: Allocator, path: []const u8) !*Bundle { | ||
| 437 | const BitcodeReader = @import("codegen/llvm/BitcodeReader.zig"); | ||
| 438 | const BlockId = enum(u32) { | ||
| 439 | Meta = 8, | ||
| 440 | Diag, | ||
| 441 | _, | ||
| 442 | }; | ||
| 443 | const RecordId = enum(u32) { | ||
| 444 | Version = 1, | ||
| 445 | DiagInfo, | ||
| 446 | SrcRange, | ||
| 447 | DiagFlag, | ||
| 448 | CatName, | ||
| 449 | FileName, | ||
| 450 | FixIt, | ||
| 451 | _, | ||
| 452 | }; | ||
| 453 | const WipDiag = struct { | ||
| 454 | level: u32 = 0, | ||
| 455 | category: u32 = 0, | ||
| 456 | msg: []const u8 = &.{}, | ||
| 457 | src_loc: SrcLoc = .{}, | ||
| 458 | src_ranges: std.ArrayListUnmanaged(SrcRange) = .{}, | ||
| 459 | sub_diags: std.ArrayListUnmanaged(Diag) = .{}, | ||
| 460 | |||
| 461 | fn deinit(wip_diag: *@This(), allocator: Allocator) void { | ||
| 462 | allocator.free(wip_diag.msg); | ||
| 463 | wip_diag.src_ranges.deinit(allocator); | ||
| 464 | for (wip_diag.sub_diags.items) |*sub_diag| sub_diag.deinit(allocator); | ||
| 465 | wip_diag.sub_diags.deinit(allocator); | ||
| 466 | wip_diag.* = undefined; | ||
| 467 | } | ||
| 468 | }; | ||
| 469 | |||
| 470 | const file = try std.fs.cwd().openFile(path, .{}); | ||
| 471 | defer file.close(); | ||
| 472 | var br = std.io.bufferedReader(file.reader()); | ||
| 473 | const reader = br.reader(); | ||
| 474 | var bc = BitcodeReader.init(gpa, .{ .reader = reader.any() }); | ||
| 475 | defer bc.deinit(); | ||
| 476 | |||
| 477 | var file_names: std.AutoHashMapUnmanaged(u32, []const u8) = .{}; | ||
| 478 | errdefer { | ||
| 479 | var file_name_it = file_names.valueIterator(); | ||
| 480 | while (file_name_it.next()) |file_name| gpa.free(file_name.*); | ||
| 481 | file_names.deinit(gpa); | ||
| 482 | } | ||
| 483 | |||
| 484 | var category_names: std.AutoHashMapUnmanaged(u32, []const u8) = .{}; | ||
| 485 | errdefer { | ||
| 486 | var category_name_it = category_names.valueIterator(); | ||
| 487 | while (category_name_it.next()) |category_name| gpa.free(category_name.*); | ||
| 488 | category_names.deinit(gpa); | ||
| 489 | } | ||
| 490 | |||
| 491 | var stack: std.ArrayListUnmanaged(WipDiag) = .{}; | ||
| 492 | defer { | ||
| 493 | for (stack.items) |*wip_diag| wip_diag.deinit(gpa); | ||
| 494 | stack.deinit(gpa); | ||
| 495 | } | ||
| 496 | try stack.append(gpa, .{}); | ||
| 497 | |||
| 498 | try bc.checkMagic("DIAG"); | ||
| 499 | while (try bc.next()) |item| switch (item) { | ||
| 500 | .start_block => |block| switch (@as(BlockId, @enumFromInt(block.id))) { | ||
| 501 | .Meta => if (stack.items.len > 0) try bc.skipBlock(block), | ||
| 502 | .Diag => try stack.append(gpa, .{}), | ||
| 503 | _ => try bc.skipBlock(block), | ||
| 504 | }, | ||
| 505 | .record => |record| switch (@as(RecordId, @enumFromInt(record.id))) { | ||
| 506 | .Version => if (record.operands[0] != 2) return error.InvalidVersion, | ||
| 507 | .DiagInfo => { | ||
| 508 | const top = &stack.items[stack.items.len - 1]; | ||
| 509 | top.level = @intCast(record.operands[0]); | ||
| 510 | top.src_loc = .{ | ||
| 511 | .file = @intCast(record.operands[1]), | ||
| 512 | .line = @intCast(record.operands[2]), | ||
| 513 | .column = @intCast(record.operands[3]), | ||
| 514 | .offset = @intCast(record.operands[4]), | ||
| 515 | }; | ||
| 516 | top.category = @intCast(record.operands[5]); | ||
| 517 | top.msg = try gpa.dupe(u8, record.blob); | ||
| 518 | }, | ||
| 519 | .SrcRange => try stack.items[stack.items.len - 1].src_ranges.append(gpa, .{ | ||
| 520 | .start = .{ | ||
| 521 | .file = @intCast(record.operands[0]), | ||
| 522 | .line = @intCast(record.operands[1]), | ||
| 523 | .column = @intCast(record.operands[2]), | ||
| 524 | .offset = @intCast(record.operands[3]), | ||
| 525 | }, | ||
| 526 | .end = .{ | ||
| 527 | .file = @intCast(record.operands[4]), | ||
| 528 | .line = @intCast(record.operands[5]), | ||
| 529 | .column = @intCast(record.operands[6]), | ||
| 530 | .offset = @intCast(record.operands[7]), | ||
| 531 | }, | ||
| 532 | }), | ||
| 533 | .DiagFlag => {}, | ||
| 534 | .CatName => { | ||
| 535 | try category_names.ensureUnusedCapacity(gpa, 1); | ||
| 536 | category_names.putAssumeCapacity( | ||
| 537 | @intCast(record.operands[0]), | ||
| 538 | try gpa.dupe(u8, record.blob), | ||
| 539 | ); | ||
| 540 | }, | ||
| 541 | .FileName => { | ||
| 542 | try file_names.ensureUnusedCapacity(gpa, 1); | ||
| 543 | file_names.putAssumeCapacity( | ||
| 544 | @intCast(record.operands[0]), | ||
| 545 | try gpa.dupe(u8, record.blob), | ||
| 546 | ); | ||
| 547 | }, | ||
| 548 | .FixIt => {}, | ||
| 549 | _ => {}, | ||
| 550 | }, | ||
| 551 | .end_block => |block| switch (@as(BlockId, @enumFromInt(block.id))) { | ||
| 552 | .Meta => {}, | ||
| 553 | .Diag => { | ||
| 554 | var wip_diag = stack.pop(); | ||
| 555 | errdefer wip_diag.deinit(gpa); | ||
| 556 | |||
| 557 | const src_ranges = try wip_diag.src_ranges.toOwnedSlice(gpa); | ||
| 558 | errdefer gpa.free(src_ranges); | ||
| 559 | |||
| 560 | const sub_diags = try wip_diag.sub_diags.toOwnedSlice(gpa); | ||
| 561 | errdefer { | ||
| 562 | for (sub_diags) |*sub_diag| sub_diag.deinit(gpa); | ||
| 563 | gpa.free(sub_diags); | ||
| 564 | } | ||
| 565 | |||
| 566 | try stack.items[stack.items.len - 1].sub_diags.append(gpa, .{ | ||
| 567 | .level = wip_diag.level, | ||
| 568 | .category = wip_diag.category, | ||
| 569 | .msg = wip_diag.msg, | ||
| 570 | .src_loc = wip_diag.src_loc, | ||
| 571 | .src_ranges = src_ranges, | ||
| 572 | .sub_diags = sub_diags, | ||
| 573 | }); | ||
| 574 | }, | ||
| 575 | _ => {}, | ||
| 576 | }, | ||
| 577 | }; | ||
| 578 | |||
| 579 | const bundle = try gpa.create(Bundle); | ||
| 580 | assert(stack.items.len == 1); | ||
| 581 | bundle.* = .{ | ||
| 582 | .file_names = file_names, | ||
| 583 | .category_names = category_names, | ||
| 584 | .diags = try stack.items[0].sub_diags.toOwnedSlice(gpa), | ||
| 585 | }; | ||
| 586 | return bundle; | ||
| 587 | } | ||
| 588 | |||
| 589 | pub fn addToErrorBundle(bundle: Bundle, eb: *ErrorBundle.Wip) !void { | ||
| 590 | for (bundle.diags) |diag| { | ||
| 591 | const notes_len = diag.count() - 1; | ||
| 592 | try eb.addRootErrorMessage(try diag.toErrorMessage(eb, bundle, notes_len)); | ||
| 593 | if (notes_len > 0) { | ||
| 594 | var note = try eb.reserveNotes(notes_len); | ||
| 595 | for (diag.sub_diags) |sub_diag| | ||
| 596 | try sub_diag.addToErrorBundle(eb, bundle, &note); | ||
| 597 | } | ||
| 598 | } | ||
| 599 | } | ||
| 600 | }; | ||
| 330 | }; | 601 | }; |
| 331 | 602 | ||
| 332 | /// Returns if there was failure. | 603 | /// Returns if there was failure. |
| ... | @@ -2826,11 +3097,16 @@ fn addBuf(bufs_list: []std.os.iovec_const, bufs_len: *usize, buf: []const u8) vo | ... | @@ -2826,11 +3097,16 @@ fn addBuf(bufs_list: []std.os.iovec_const, bufs_len: *usize, buf: []const u8) vo |
| 2826 | 3097 | ||
| 2827 | /// This function is temporally single-threaded. | 3098 | /// This function is temporally single-threaded. |
| 2828 | pub fn totalErrorCount(self: *Compilation) u32 { | 3099 | pub fn totalErrorCount(self: *Compilation) u32 { |
| 2829 | var total: usize = self.failed_c_objects.count() + | 3100 | var total: usize = |
| 2830 | self.misc_failures.count() + | 3101 | self.misc_failures.count() + |
| 2831 | @intFromBool(self.alloc_failure_occurred) + | 3102 | @intFromBool(self.alloc_failure_occurred) + |
| 2832 | self.lld_errors.items.len; | 3103 | self.lld_errors.items.len; |
| 2833 | 3104 | ||
| 3105 | { | ||
| 3106 | var it = self.failed_c_objects.iterator(); | ||
| 3107 | while (it.next()) |entry| total += entry.value_ptr.*.diags.len; | ||
| 3108 | } | ||
| 3109 | |||
| 2834 | if (!build_options.only_core_functionality) { | 3110 | if (!build_options.only_core_functionality) { |
| 2835 | for (self.failed_win32_resources.values()) |errs| { | 3111 | for (self.failed_win32_resources.values()) |errs| { |
| 2836 | total += errs.errorMessageCount(); | 3112 | total += errs.errorMessageCount(); |
| ... | @@ -2911,24 +3187,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { | ... | @@ -2911,24 +3187,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { |
| 2911 | 3187 | ||
| 2912 | { | 3188 | { |
| 2913 | var it = self.failed_c_objects.iterator(); | 3189 | var it = self.failed_c_objects.iterator(); |
| 2914 | while (it.next()) |entry| { | 3190 | while (it.next()) |entry| try entry.value_ptr.*.addToErrorBundle(&bundle); |
| 2915 | const c_object = entry.key_ptr.*; | ||
| 2916 | const err_msg = entry.value_ptr.*; | ||
| 2917 | // TODO these fields will need to be adjusted when we have proper | ||
| 2918 | // C error reporting bubbling up. | ||
| 2919 | try bundle.addRootErrorMessage(.{ | ||
| 2920 | .msg = try bundle.printString("unable to build C object: {s}", .{err_msg.msg}), | ||
| 2921 | .src_loc = try bundle.addSourceLocation(.{ | ||
| 2922 | .src_path = try bundle.addString(c_object.src.src_path), | ||
| 2923 | .span_start = 0, | ||
| 2924 | .span_main = 0, | ||
| 2925 | .span_end = 1, | ||
| 2926 | .line = err_msg.line, | ||
| 2927 | .column = err_msg.column, | ||
| 2928 | .source_line = 0, // TODO | ||
| 2929 | }), | ||
| 2930 | }); | ||
| 2931 | } | ||
| 2932 | } | 3191 | } |
| 2933 | 3192 | ||
| 2934 | if (!build_options.only_core_functionality) { | 3193 | if (!build_options.only_core_functionality) { |
| ... | @@ -4209,19 +4468,9 @@ fn reportRetryableCObjectError( | ... | @@ -4209,19 +4468,9 @@ fn reportRetryableCObjectError( |
| 4209 | ) error{OutOfMemory}!void { | 4468 | ) error{OutOfMemory}!void { |
| 4210 | c_object.status = .failure_retryable; | 4469 | c_object.status = .failure_retryable; |
| 4211 | 4470 | ||
| 4212 | const c_obj_err_msg = try comp.gpa.create(CObject.ErrorMsg); | 4471 | switch (comp.failCObj(c_object, "{s}", .{@errorName(err)})) { |
| 4213 | errdefer comp.gpa.destroy(c_obj_err_msg); | 4472 | error.AnalysisFail => return, |
| 4214 | const msg = try std.fmt.allocPrint(comp.gpa, "{s}", .{@errorName(err)}); | 4473 | else => |e| return e, |
| 4215 | errdefer comp.gpa.free(msg); | ||
| 4216 | c_obj_err_msg.* = .{ | ||
| 4217 | .msg = msg, | ||
| 4218 | .line = 0, | ||
| 4219 | .column = 0, | ||
| 4220 | }; | ||
| 4221 | { | ||
| 4222 | comp.mutex.lock(); | ||
| 4223 | defer comp.mutex.unlock(); | ||
| 4224 | try comp.failed_c_objects.putNoClobber(comp.gpa, c_object, c_obj_err_msg); | ||
| 4225 | } | 4474 | } |
| 4226 | } | 4475 | } |
| 4227 | 4476 | ||
| ... | @@ -4457,6 +4706,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P | ... | @@ -4457,6 +4706,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 4457 | // We can't know the digest until we do the C compiler invocation, | 4706 | // We can't know the digest until we do the C compiler invocation, |
| 4458 | // so we need a temporary filename. | 4707 | // so we need a temporary filename. |
| 4459 | const out_obj_path = try comp.tmpFilePath(arena, o_basename); | 4708 | const out_obj_path = try comp.tmpFilePath(arena, o_basename); |
| 4709 | const out_diag_path = try std.fmt.allocPrint(arena, "{s}.diag", .{out_obj_path}); | ||
| 4460 | var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); | 4710 | var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); |
| 4461 | defer zig_cache_tmp_dir.close(); | 4711 | defer zig_cache_tmp_dir.close(); |
| 4462 | 4712 | ||
| ... | @@ -4470,18 +4720,20 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P | ... | @@ -4470,18 +4720,20 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 4470 | 4720 | ||
| 4471 | try argv.ensureUnusedCapacity(5); | 4721 | try argv.ensureUnusedCapacity(5); |
| 4472 | switch (comp.clang_preprocessor_mode) { | 4722 | switch (comp.clang_preprocessor_mode) { |
| 4473 | .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }), | 4723 | .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }), |
| 4474 | .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }), | 4724 | .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }), |
| 4475 | .stdout => argv.appendAssumeCapacity("-E"), | 4725 | .stdout => argv.appendAssumeCapacity("-E"), |
| 4476 | } | 4726 | } |
| 4477 | if (comp.clang_passthrough_mode) { | 4727 | if (comp.clang_passthrough_mode) { |
| 4478 | if (comp.emit_asm != null) { | 4728 | if (comp.emit_asm != null) { |
| 4479 | argv.appendAssumeCapacity("-S"); | 4729 | argv.appendAssumeCapacity("-S"); |
| 4480 | } else if (comp.emit_llvm_ir != null) { | 4730 | } else if (comp.emit_llvm_ir != null) { |
| 4481 | argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" }); | 4731 | argv.appendSliceAssumeCapacity(&.{ "-emit-llvm", "-S" }); |
| 4482 | } else if (comp.emit_llvm_bc != null) { | 4732 | } else if (comp.emit_llvm_bc != null) { |
| 4483 | argv.appendAssumeCapacity("-emit-llvm"); | 4733 | argv.appendAssumeCapacity("-emit-llvm"); |
| 4484 | } | 4734 | } |
| 4735 | } else { | ||
| 4736 | argv.appendSliceAssumeCapacity(&.{ "--serialize-diagnostics", out_diag_path }); | ||
| 4485 | } | 4737 | } |
| 4486 | 4738 | ||
| 4487 | if (comp.verbose_cc) { | 4739 | if (comp.verbose_cc) { |
| ... | @@ -4524,10 +4776,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P | ... | @@ -4524,10 +4776,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 4524 | switch (term) { | 4776 | switch (term) { |
| 4525 | .Exited => |code| { | 4777 | .Exited => |code| { |
| 4526 | if (code != 0) { | 4778 | if (code != 0) { |
| 4527 | // TODO parse clang stderr and turn it into an error message | 4779 | const bundle = CObject.Diag.Bundle.parse(comp.gpa, out_diag_path) catch |err| { |
| 4528 | // and then call failCObjWithOwnedErrorMsg | 4780 | log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr }); |
| 4529 | log.err("clang failed with stderr: {s}", .{stderr}); | 4781 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); |
| 4530 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); | 4782 | }; |
| 4783 | return comp.failCObjWithOwnedDiagBundle(c_object, bundle); | ||
| 4531 | } | 4784 | } |
| 4532 | }, | 4785 | }, |
| 4533 | else => { | 4786 | else => { |
| ... | @@ -5413,37 +5666,45 @@ pub fn addCCArgs( | ... | @@ -5413,37 +5666,45 @@ pub fn addCCArgs( |
| 5413 | try argv.appendSlice(comp.clang_argv); | 5666 | try argv.appendSlice(comp.clang_argv); |
| 5414 | } | 5667 | } |
| 5415 | 5668 | ||
| 5416 | fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) SemaError { | 5669 | fn failCObj( |
| 5670 | comp: *Compilation, | ||
| 5671 | c_object: *CObject, | ||
| 5672 | comptime format: []const u8, | ||
| 5673 | args: anytype, | ||
| 5674 | ) SemaError { | ||
| 5417 | @setCold(true); | 5675 | @setCold(true); |
| 5418 | const err_msg = blk: { | 5676 | const diag_bundle = blk: { |
| 5419 | const msg = try std.fmt.allocPrint(comp.gpa, format, args); | 5677 | const diag_bundle = try comp.gpa.create(CObject.Diag.Bundle); |
| 5420 | errdefer comp.gpa.free(msg); | 5678 | diag_bundle.* = .{}; |
| 5421 | const err_msg = try comp.gpa.create(CObject.ErrorMsg); | 5679 | errdefer diag_bundle.destroy(comp.gpa); |
| 5422 | errdefer comp.gpa.destroy(err_msg); | 5680 | |
| 5423 | err_msg.* = .{ | 5681 | try diag_bundle.file_names.ensureTotalCapacity(comp.gpa, 1); |
| 5424 | .msg = msg, | 5682 | diag_bundle.file_names.putAssumeCapacity(1, try comp.gpa.dupe(u8, c_object.src.src_path)); |
| 5425 | .line = 0, | 5683 | |
| 5426 | .column = 0, | 5684 | diag_bundle.diags = try comp.gpa.alloc(CObject.Diag, 1); |
| 5427 | }; | 5685 | diag_bundle.diags[0] = .{}; |
| 5428 | break :blk err_msg; | 5686 | diag_bundle.diags[0].level = 3; |
| 5687 | diag_bundle.diags[0].msg = try std.fmt.allocPrint(comp.gpa, format, args); | ||
| 5688 | diag_bundle.diags[0].src_loc.file = 1; | ||
| 5689 | break :blk diag_bundle; | ||
| 5429 | }; | 5690 | }; |
| 5430 | return comp.failCObjWithOwnedErrorMsg(c_object, err_msg); | 5691 | return comp.failCObjWithOwnedDiagBundle(c_object, diag_bundle); |
| 5431 | } | 5692 | } |
| 5432 | 5693 | ||
| 5433 | fn failCObjWithOwnedErrorMsg( | 5694 | fn failCObjWithOwnedDiagBundle( |
| 5434 | comp: *Compilation, | 5695 | comp: *Compilation, |
| 5435 | c_object: *CObject, | 5696 | c_object: *CObject, |
| 5436 | err_msg: *CObject.ErrorMsg, | 5697 | diag_bundle: *CObject.Diag.Bundle, |
| 5437 | ) SemaError { | 5698 | ) SemaError { |
| 5438 | @setCold(true); | 5699 | @setCold(true); |
| 5439 | { | 5700 | { |
| 5440 | comp.mutex.lock(); | 5701 | comp.mutex.lock(); |
| 5441 | defer comp.mutex.unlock(); | 5702 | defer comp.mutex.unlock(); |
| 5442 | { | 5703 | { |
| 5443 | errdefer err_msg.destroy(comp.gpa); | 5704 | errdefer diag_bundle.destroy(comp.gpa); |
| 5444 | try comp.failed_c_objects.ensureUnusedCapacity(comp.gpa, 1); | 5705 | try comp.failed_c_objects.ensureUnusedCapacity(comp.gpa, 1); |
| 5445 | } | 5706 | } |
| 5446 | comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg); | 5707 | comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, diag_bundle); |
| 5447 | } | 5708 | } |
| 5448 | c_object.status = .failure; | 5709 | c_object.status = .failure; |
| 5449 | return error.AnalysisFail; | 5710 | return error.AnalysisFail; |
src/codegen/llvm/BitcodeReader.zig created+515| ... | @@ -0,0 +1,515 @@ | ||
| 1 | allocator: std.mem.Allocator, | ||
| 2 | record_arena: std.heap.ArenaAllocator.State, | ||
| 3 | reader: std.io.AnyReader, | ||
| 4 | keep_names: bool, | ||
| 5 | bit_buffer: u32, | ||
| 6 | bit_offset: u5, | ||
| 7 | stack: std.ArrayListUnmanaged(State), | ||
| 8 | block_info: std.AutoHashMapUnmanaged(u32, Block.Info), | ||
| 9 | |||
| 10 | pub const Item = union(enum) { | ||
| 11 | start_block: Block, | ||
| 12 | record: Record, | ||
| 13 | end_block: Block, | ||
| 14 | }; | ||
| 15 | |||
| 16 | pub const Block = struct { | ||
| 17 | name: []const u8, | ||
| 18 | id: u32, | ||
| 19 | len: u32, | ||
| 20 | |||
| 21 | const block_info: u32 = 0; | ||
| 22 | const first_reserved: u32 = 1; | ||
| 23 | const last_standard: u32 = 7; | ||
| 24 | |||
| 25 | const Info = struct { | ||
| 26 | block_name: []const u8, | ||
| 27 | record_names: std.AutoHashMapUnmanaged(u32, []const u8), | ||
| 28 | abbrevs: Abbrev.Store, | ||
| 29 | |||
| 30 | const default: Info = .{ | ||
| 31 | .block_name = &.{}, | ||
| 32 | .record_names = .{}, | ||
| 33 | .abbrevs = .{ .abbrevs = .{} }, | ||
| 34 | }; | ||
| 35 | |||
| 36 | const set_bid: u32 = 1; | ||
| 37 | const block_name: u32 = 2; | ||
| 38 | const set_record_name: u32 = 3; | ||
| 39 | |||
| 40 | fn deinit(info: *Info, allocator: std.mem.Allocator) void { | ||
| 41 | allocator.free(info.block_name); | ||
| 42 | var record_names_it = info.record_names.valueIterator(); | ||
| 43 | while (record_names_it.next()) |record_name| allocator.free(record_name.*); | ||
| 44 | info.record_names.deinit(allocator); | ||
| 45 | info.abbrevs.deinit(allocator); | ||
| 46 | info.* = undefined; | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | }; | ||
| 50 | |||
| 51 | pub const Record = struct { | ||
| 52 | name: []const u8, | ||
| 53 | id: u32, | ||
| 54 | operands: []u64, | ||
| 55 | blob: []u8, | ||
| 56 | |||
| 57 | fn toOwnedAbbrev(record: Record, allocator: std.mem.Allocator) !Abbrev { | ||
| 58 | var operands = std.ArrayList(Abbrev.Operand).init(allocator); | ||
| 59 | defer operands.deinit(); | ||
| 60 | |||
| 61 | assert(record.id == Abbrev.Builtin.define_abbrev.toRecordId()); | ||
| 62 | var i: usize = 0; | ||
| 63 | while (i < record.operands.len) switch (record.operands[i]) { | ||
| 64 | Abbrev.Operand.literal => { | ||
| 65 | try operands.append(.{ .literal = record.operands[i + 1] }); | ||
| 66 | i += 2; | ||
| 67 | }, | ||
| 68 | @intFromEnum(Abbrev.Operand.Encoding.fixed) => { | ||
| 69 | try operands.append(.{ .encoding = .{ .fixed = @intCast(record.operands[i + 1]) } }); | ||
| 70 | i += 2; | ||
| 71 | }, | ||
| 72 | @intFromEnum(Abbrev.Operand.Encoding.vbr) => { | ||
| 73 | try operands.append(.{ .encoding = .{ .vbr = @intCast(record.operands[i + 1]) } }); | ||
| 74 | i += 2; | ||
| 75 | }, | ||
| 76 | @intFromEnum(Abbrev.Operand.Encoding.array) => { | ||
| 77 | try operands.append(.{ .encoding = .{ .array = 6 } }); | ||
| 78 | i += 1; | ||
| 79 | }, | ||
| 80 | @intFromEnum(Abbrev.Operand.Encoding.char6) => { | ||
| 81 | try operands.append(.{ .encoding = .char6 }); | ||
| 82 | i += 1; | ||
| 83 | }, | ||
| 84 | @intFromEnum(Abbrev.Operand.Encoding.blob) => { | ||
| 85 | try operands.append(.{ .encoding = .{ .blob = 6 } }); | ||
| 86 | i += 1; | ||
| 87 | }, | ||
| 88 | else => unreachable, | ||
| 89 | }; | ||
| 90 | |||
| 91 | return .{ .operands = try operands.toOwnedSlice() }; | ||
| 92 | } | ||
| 93 | }; | ||
| 94 | |||
| 95 | pub const InitOptions = struct { | ||
| 96 | reader: std.io.AnyReader, | ||
| 97 | keep_names: bool = false, | ||
| 98 | }; | ||
| 99 | pub fn init(allocator: std.mem.Allocator, options: InitOptions) BitcodeReader { | ||
| 100 | return .{ | ||
| 101 | .allocator = allocator, | ||
| 102 | .record_arena = .{}, | ||
| 103 | .reader = options.reader, | ||
| 104 | .keep_names = options.keep_names, | ||
| 105 | .bit_buffer = 0, | ||
| 106 | .bit_offset = 0, | ||
| 107 | .stack = .{}, | ||
| 108 | .block_info = .{}, | ||
| 109 | }; | ||
| 110 | } | ||
| 111 | |||
| 112 | pub fn deinit(bc: *BitcodeReader) void { | ||
| 113 | var block_info_it = bc.block_info.valueIterator(); | ||
| 114 | while (block_info_it.next()) |block_info| block_info.deinit(bc.allocator); | ||
| 115 | bc.block_info.deinit(bc.allocator); | ||
| 116 | for (bc.stack.items) |*state| state.deinit(bc.allocator); | ||
| 117 | bc.stack.deinit(bc.allocator); | ||
| 118 | bc.record_arena.promote(bc.allocator).deinit(); | ||
| 119 | bc.* = undefined; | ||
| 120 | } | ||
| 121 | |||
| 122 | pub fn checkMagic(bc: *BitcodeReader, magic: *const [4]u8) !void { | ||
| 123 | var buffer: [4]u8 = undefined; | ||
| 124 | try bc.readBytes(&buffer); | ||
| 125 | if (!std.mem.eql(u8, &buffer, magic)) return error.InvalidMagic; | ||
| 126 | |||
| 127 | try bc.startBlock(null, 2); | ||
| 128 | try bc.block_info.put(bc.allocator, Block.block_info, Block.Info.default); | ||
| 129 | } | ||
| 130 | |||
| 131 | pub fn next(bc: *BitcodeReader) !?Item { | ||
| 132 | while (true) { | ||
| 133 | const record = (try bc.nextRecord()) orelse | ||
| 134 | return if (bc.stack.items.len > 1) error.EndOfStream else null; | ||
| 135 | switch (record.id) { | ||
| 136 | else => return .{ .record = record }, | ||
| 137 | Abbrev.Builtin.end_block.toRecordId() => { | ||
| 138 | const block_id = bc.stack.items[bc.stack.items.len - 1].block_id.?; | ||
| 139 | try bc.endBlock(); | ||
| 140 | return .{ .end_block = .{ | ||
| 141 | .name = if (bc.block_info.get(block_id)) |block_info| | ||
| 142 | block_info.block_name | ||
| 143 | else | ||
| 144 | &.{}, | ||
| 145 | .id = block_id, | ||
| 146 | .len = 0, | ||
| 147 | } }; | ||
| 148 | }, | ||
| 149 | Abbrev.Builtin.enter_subblock.toRecordId() => { | ||
| 150 | const block_id: u32 = @intCast(record.operands[0]); | ||
| 151 | switch (block_id) { | ||
| 152 | Block.block_info => try bc.parseBlockInfoBlock(), | ||
| 153 | Block.first_reserved...Block.last_standard => return error.UnsupportedBlockId, | ||
| 154 | else => { | ||
| 155 | try bc.startBlock(block_id, @intCast(record.operands[1])); | ||
| 156 | return .{ .start_block = .{ | ||
| 157 | .name = if (bc.block_info.get(block_id)) |block_info| | ||
| 158 | block_info.block_name | ||
| 159 | else | ||
| 160 | &.{}, | ||
| 161 | .id = block_id, | ||
| 162 | .len = @intCast(record.operands[2]), | ||
| 163 | } }; | ||
| 164 | }, | ||
| 165 | } | ||
| 166 | }, | ||
| 167 | Abbrev.Builtin.define_abbrev.toRecordId() => try bc.stack.items[bc.stack.items.len - 1] | ||
| 168 | .abbrevs.addOwnedAbbrev(bc.allocator, try record.toOwnedAbbrev(bc.allocator)), | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | pub fn skipBlock(bc: *BitcodeReader, block: Block) !void { | ||
| 174 | assert(bc.bit_offset == 0); | ||
| 175 | try bc.reader.skipBytes(@as(u34, block.len) * 4, .{}); | ||
| 176 | try bc.endBlock(); | ||
| 177 | } | ||
| 178 | |||
| 179 | fn nextRecord(bc: *BitcodeReader) !?Record { | ||
| 180 | const state = &bc.stack.items[bc.stack.items.len - 1]; | ||
| 181 | const abbrev_id = bc.readFixed(u32, state.abbrev_id_width) catch |err| switch (err) { | ||
| 182 | error.EndOfStream => return null, | ||
| 183 | else => |e| return e, | ||
| 184 | }; | ||
| 185 | if (abbrev_id >= state.abbrevs.abbrevs.items.len) return error.InvalidAbbrevId; | ||
| 186 | const abbrev = state.abbrevs.abbrevs.items[abbrev_id]; | ||
| 187 | |||
| 188 | var record_arena = bc.record_arena.promote(bc.allocator); | ||
| 189 | defer bc.record_arena = record_arena.state; | ||
| 190 | _ = record_arena.reset(.retain_capacity); | ||
| 191 | |||
| 192 | var operands = try std.ArrayList(u64).initCapacity(record_arena.allocator(), abbrev.operands.len); | ||
| 193 | var blob = std.ArrayList(u8).init(record_arena.allocator()); | ||
| 194 | for (abbrev.operands, 0..) |abbrev_operand, abbrev_operand_i| switch (abbrev_operand) { | ||
| 195 | .literal => |value| operands.appendAssumeCapacity(value), | ||
| 196 | .encoding => |abbrev_encoding| switch (abbrev_encoding) { | ||
| 197 | .fixed => |width| operands.appendAssumeCapacity(try bc.readFixed(u64, width)), | ||
| 198 | .vbr => |width| operands.appendAssumeCapacity(try bc.readVbr(u64, width)), | ||
| 199 | .array => |len_width| { | ||
| 200 | assert(abbrev_operand_i + 2 == abbrev.operands.len); | ||
| 201 | const len: usize = @intCast(try bc.readVbr(u32, len_width)); | ||
| 202 | try operands.ensureUnusedCapacity(len); | ||
| 203 | for (0..len) |_| switch (abbrev.operands[abbrev.operands.len - 1]) { | ||
| 204 | .literal => |elem_value| operands.appendAssumeCapacity(elem_value), | ||
| 205 | .encoding => |elem_encoding| switch (elem_encoding) { | ||
| 206 | .fixed => |elem_width| operands.appendAssumeCapacity(try bc.readFixed(u64, elem_width)), | ||
| 207 | .vbr => |elem_width| operands.appendAssumeCapacity(try bc.readVbr(u64, elem_width)), | ||
| 208 | .array, .blob => return error.InvalidArrayElement, | ||
| 209 | .char6 => operands.appendAssumeCapacity(try bc.readChar6()), | ||
| 210 | }, | ||
| 211 | .align_32_bits, .block_len => return error.UnsupportedArrayElement, | ||
| 212 | .abbrev_op => switch (try bc.readFixed(u1, 1)) { | ||
| 213 | 1 => try operands.appendSlice(&.{ | ||
| 214 | Abbrev.Operand.literal, | ||
| 215 | try bc.readVbr(u64, 8), | ||
| 216 | }), | ||
| 217 | 0 => { | ||
| 218 | const encoding: Abbrev.Operand.Encoding = | ||
| 219 | @enumFromInt(try bc.readFixed(u3, 3)); | ||
| 220 | try operands.append(@intFromEnum(encoding)); | ||
| 221 | switch (encoding) { | ||
| 222 | .fixed, .vbr => try operands.append(try bc.readVbr(u7, 5)), | ||
| 223 | .array, .char6, .blob => {}, | ||
| 224 | _ => return error.UnsuportedAbbrevEncoding, | ||
| 225 | } | ||
| 226 | }, | ||
| 227 | }, | ||
| 228 | }; | ||
| 229 | break; | ||
| 230 | }, | ||
| 231 | .char6 => operands.appendAssumeCapacity(try bc.readChar6()), | ||
| 232 | .blob => |len_width| { | ||
| 233 | assert(abbrev_operand_i + 1 == abbrev.operands.len); | ||
| 234 | const len = std.math.cast(usize, try bc.readVbr(u32, len_width)) orelse | ||
| 235 | return error.Overflow; | ||
| 236 | bc.align32Bits(); | ||
| 237 | try bc.readBytes(try blob.addManyAsSlice(len)); | ||
| 238 | bc.align32Bits(); | ||
| 239 | }, | ||
| 240 | }, | ||
| 241 | .align_32_bits => bc.align32Bits(), | ||
| 242 | .block_len => operands.appendAssumeCapacity(try bc.read32Bits()), | ||
| 243 | .abbrev_op => unreachable, | ||
| 244 | }; | ||
| 245 | return .{ | ||
| 246 | .name = name: { | ||
| 247 | if (operands.items.len < 1) break :name &.{}; | ||
| 248 | const record_id = std.math.cast(u32, operands.items[0]) orelse break :name &.{}; | ||
| 249 | if (state.block_id) |block_id| { | ||
| 250 | if (bc.block_info.get(block_id)) |block_info| { | ||
| 251 | break :name block_info.record_names.get(record_id) orelse break :name &.{}; | ||
| 252 | } | ||
| 253 | } | ||
| 254 | break :name &.{}; | ||
| 255 | }, | ||
| 256 | .id = std.math.cast(u32, operands.items[0]) orelse return error.InvalidRecordId, | ||
| 257 | .operands = operands.items[1..], | ||
| 258 | .blob = blob.items, | ||
| 259 | }; | ||
| 260 | } | ||
| 261 | |||
| 262 | fn startBlock(bc: *BitcodeReader, block_id: ?u32, new_abbrev_len: u6) !void { | ||
| 263 | const abbrevs = if (block_id) |id| | ||
| 264 | if (bc.block_info.get(id)) |block_info| block_info.abbrevs.abbrevs.items else &.{} | ||
| 265 | else | ||
| 266 | &.{}; | ||
| 267 | |||
| 268 | const state = try bc.stack.addOne(bc.allocator); | ||
| 269 | state.* = .{ | ||
| 270 | .block_id = block_id, | ||
| 271 | .abbrev_id_width = new_abbrev_len, | ||
| 272 | .abbrevs = .{ .abbrevs = .{} }, | ||
| 273 | }; | ||
| 274 | try state.abbrevs.abbrevs.ensureTotalCapacity( | ||
| 275 | bc.allocator, | ||
| 276 | @typeInfo(Abbrev.Builtin).Enum.fields.len + abbrevs.len, | ||
| 277 | ); | ||
| 278 | |||
| 279 | assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.end_block)); | ||
| 280 | try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ | ||
| 281 | .operands = &.{ | ||
| 282 | .{ .literal = Abbrev.Builtin.end_block.toRecordId() }, | ||
| 283 | .align_32_bits, | ||
| 284 | }, | ||
| 285 | }); | ||
| 286 | assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.enter_subblock)); | ||
| 287 | try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ | ||
| 288 | .operands = &.{ | ||
| 289 | .{ .literal = Abbrev.Builtin.enter_subblock.toRecordId() }, | ||
| 290 | .{ .encoding = .{ .vbr = 8 } }, // blockid | ||
| 291 | .{ .encoding = .{ .vbr = 4 } }, // newabbrevlen | ||
| 292 | .align_32_bits, | ||
| 293 | .block_len, | ||
| 294 | }, | ||
| 295 | }); | ||
| 296 | assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.define_abbrev)); | ||
| 297 | try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ | ||
| 298 | .operands = &.{ | ||
| 299 | .{ .literal = Abbrev.Builtin.define_abbrev.toRecordId() }, | ||
| 300 | .{ .encoding = .{ .array = 5 } }, // numabbrevops | ||
| 301 | .abbrev_op, | ||
| 302 | }, | ||
| 303 | }); | ||
| 304 | assert(state.abbrevs.abbrevs.items.len == @intFromEnum(Abbrev.Builtin.unabbrev_record)); | ||
| 305 | try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, .{ | ||
| 306 | .operands = &.{ | ||
| 307 | .{ .encoding = .{ .vbr = 6 } }, // code | ||
| 308 | .{ .encoding = .{ .array = 6 } }, // numops | ||
| 309 | .{ .encoding = .{ .vbr = 6 } }, // ops | ||
| 310 | }, | ||
| 311 | }); | ||
| 312 | assert(state.abbrevs.abbrevs.items.len == @typeInfo(Abbrev.Builtin).Enum.fields.len); | ||
| 313 | for (abbrevs) |abbrev| try state.abbrevs.addAbbrevAssumeCapacity(bc.allocator, abbrev); | ||
| 314 | } | ||
| 315 | |||
| 316 | fn endBlock(bc: *BitcodeReader) !void { | ||
| 317 | if (bc.stack.items.len == 0) return error.InvalidEndBlock; | ||
| 318 | bc.stack.items[bc.stack.items.len - 1].deinit(bc.allocator); | ||
| 319 | bc.stack.items.len -= 1; | ||
| 320 | } | ||
| 321 | |||
| 322 | fn parseBlockInfoBlock(bc: *BitcodeReader) !void { | ||
| 323 | var block_id: ?u32 = null; | ||
| 324 | while (true) { | ||
| 325 | const record = (try bc.nextRecord()) orelse return error.EndOfStream; | ||
| 326 | switch (record.id) { | ||
| 327 | Abbrev.Builtin.end_block.toRecordId() => break, | ||
| 328 | Abbrev.Builtin.define_abbrev.toRecordId() => { | ||
| 329 | const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse | ||
| 330 | return error.UnspecifiedBlockId); | ||
| 331 | if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; | ||
| 332 | try gop.value_ptr.abbrevs.addOwnedAbbrev( | ||
| 333 | bc.allocator, | ||
| 334 | try record.toOwnedAbbrev(bc.allocator), | ||
| 335 | ); | ||
| 336 | }, | ||
| 337 | Block.Info.set_bid => block_id = std.math.cast(u32, record.operands[0]) orelse | ||
| 338 | return error.Overflow, | ||
| 339 | Block.Info.block_name => if (bc.keep_names) { | ||
| 340 | const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse | ||
| 341 | return error.UnspecifiedBlockId); | ||
| 342 | if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; | ||
| 343 | const name = try bc.allocator.alloc(u8, record.operands.len); | ||
| 344 | errdefer bc.allocator.free(name); | ||
| 345 | for (name, record.operands) |*byte, operand| | ||
| 346 | byte.* = std.math.cast(u8, operand) orelse return error.InvalidName; | ||
| 347 | gop.value_ptr.block_name = name; | ||
| 348 | }, | ||
| 349 | Block.Info.set_record_name => if (bc.keep_names) { | ||
| 350 | const gop = try bc.block_info.getOrPut(bc.allocator, block_id orelse | ||
| 351 | return error.UnspecifiedBlockId); | ||
| 352 | if (!gop.found_existing) gop.value_ptr.* = Block.Info.default; | ||
| 353 | const name = try bc.allocator.alloc(u8, record.operands.len - 1); | ||
| 354 | errdefer bc.allocator.free(name); | ||
| 355 | for (name, record.operands[1..]) |*byte, operand| | ||
| 356 | byte.* = std.math.cast(u8, operand) orelse return error.InvalidName; | ||
| 357 | try gop.value_ptr.record_names.put( | ||
| 358 | bc.allocator, | ||
| 359 | std.math.cast(u32, record.operands[0]) orelse return error.Overflow, | ||
| 360 | name, | ||
| 361 | ); | ||
| 362 | }, | ||
| 363 | else => return error.UnsupportedBlockInfoRecord, | ||
| 364 | } | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | fn align32Bits(bc: *BitcodeReader) void { | ||
| 369 | bc.bit_offset = 0; | ||
| 370 | } | ||
| 371 | |||
| 372 | fn read32Bits(bc: *BitcodeReader) !u32 { | ||
| 373 | assert(bc.bit_offset == 0); | ||
| 374 | return bc.reader.readInt(u32, .little); | ||
| 375 | } | ||
| 376 | |||
| 377 | fn readBytes(bc: *BitcodeReader, bytes: []u8) !void { | ||
| 378 | assert(bc.bit_offset == 0); | ||
| 379 | try bc.reader.readNoEof(bytes); | ||
| 380 | |||
| 381 | const trailing_bytes = bytes.len % 4; | ||
| 382 | if (trailing_bytes > 0) { | ||
| 383 | var bit_buffer = [1]u8{0} ** 4; | ||
| 384 | try bc.reader.readNoEof(bit_buffer[trailing_bytes..]); | ||
| 385 | bc.bit_buffer = std.mem.readInt(u32, &bit_buffer, .little); | ||
| 386 | bc.bit_offset = @intCast(trailing_bytes * 8); | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | fn readFixed(bc: *BitcodeReader, comptime T: type, bits: u7) !T { | ||
| 391 | var result: T = 0; | ||
| 392 | var shift: std.math.Log2IntCeil(T) = 0; | ||
| 393 | var remaining = bits; | ||
| 394 | while (remaining > 0) { | ||
| 395 | if (bc.bit_offset == 0) bc.bit_buffer = try bc.read32Bits(); | ||
| 396 | const chunk_len = @min(@as(u6, 32) - bc.bit_offset, remaining); | ||
| 397 | const chunk_mask = @as(u32, std.math.maxInt(u32)) >> @intCast(32 - chunk_len); | ||
| 398 | result |= @as(T, @intCast(bc.bit_buffer >> bc.bit_offset & chunk_mask)) << @intCast(shift); | ||
| 399 | shift += @intCast(chunk_len); | ||
| 400 | remaining -= chunk_len; | ||
| 401 | bc.bit_offset = @truncate(bc.bit_offset + chunk_len); | ||
| 402 | } | ||
| 403 | return result; | ||
| 404 | } | ||
| 405 | |||
| 406 | fn readVbr(bc: *BitcodeReader, comptime T: type, bits: u7) !T { | ||
| 407 | const chunk_bits: u6 = @intCast(bits - 1); | ||
| 408 | const chunk_msb = @as(u64, 1) << chunk_bits; | ||
| 409 | |||
| 410 | var result: u64 = 0; | ||
| 411 | var shift: u6 = 0; | ||
| 412 | while (true) { | ||
| 413 | var chunk = try bc.readFixed(u64, bits); | ||
| 414 | result |= (chunk & (chunk_msb - 1)) << shift; | ||
| 415 | if (chunk & chunk_msb == 0) break; | ||
| 416 | shift += chunk_bits; | ||
| 417 | } | ||
| 418 | return @intCast(result); | ||
| 419 | } | ||
| 420 | |||
| 421 | fn readChar6(bc: *BitcodeReader) !u8 { | ||
| 422 | return switch (try bc.readFixed(u6, 6)) { | ||
| 423 | 0...25 => |c| @as(u8, c - 0) + 'a', | ||
| 424 | 26...51 => |c| @as(u8, c - 26) + 'A', | ||
| 425 | 52...61 => |c| @as(u8, c - 52) + '0', | ||
| 426 | 62 => '.', | ||
| 427 | 63 => '_', | ||
| 428 | }; | ||
| 429 | } | ||
| 430 | |||
| 431 | const State = struct { | ||
| 432 | block_id: ?u32, | ||
| 433 | abbrev_id_width: u6, | ||
| 434 | abbrevs: Abbrev.Store, | ||
| 435 | |||
| 436 | fn deinit(state: *State, allocator: std.mem.Allocator) void { | ||
| 437 | state.abbrevs.deinit(allocator); | ||
| 438 | state.* = undefined; | ||
| 439 | } | ||
| 440 | }; | ||
| 441 | |||
| 442 | const Abbrev = struct { | ||
| 443 | operands: []const Operand, | ||
| 444 | |||
| 445 | const Builtin = enum(u2) { | ||
| 446 | end_block, | ||
| 447 | enter_subblock, | ||
| 448 | define_abbrev, | ||
| 449 | unabbrev_record, | ||
| 450 | |||
| 451 | const first_record_id: u32 = std.math.maxInt(u32) - @typeInfo(Builtin).Enum.fields.len + 1; | ||
| 452 | fn toRecordId(builtin: Builtin) u32 { | ||
| 453 | return first_record_id + @intFromEnum(builtin); | ||
| 454 | } | ||
| 455 | }; | ||
| 456 | |||
| 457 | const Operand = union(enum) { | ||
| 458 | literal: u64, | ||
| 459 | encoding: union(Encoding) { | ||
| 460 | fixed: u7, | ||
| 461 | vbr: u6, | ||
| 462 | array: u3, | ||
| 463 | char6, | ||
| 464 | blob: u3, | ||
| 465 | }, | ||
| 466 | align_32_bits, | ||
| 467 | block_len, | ||
| 468 | abbrev_op, | ||
| 469 | |||
| 470 | const literal = std.math.maxInt(u64); | ||
| 471 | const Encoding = enum(u3) { | ||
| 472 | fixed = 1, | ||
| 473 | vbr = 2, | ||
| 474 | array = 3, | ||
| 475 | char6 = 4, | ||
| 476 | blob = 5, | ||
| 477 | _, | ||
| 478 | }; | ||
| 479 | }; | ||
| 480 | |||
| 481 | const Store = struct { | ||
| 482 | abbrevs: std.ArrayListUnmanaged(Abbrev), | ||
| 483 | |||
| 484 | fn deinit(store: *Store, allocator: std.mem.Allocator) void { | ||
| 485 | for (store.abbrevs.items) |abbrev| allocator.free(abbrev.operands); | ||
| 486 | store.abbrevs.deinit(allocator); | ||
| 487 | store.* = undefined; | ||
| 488 | } | ||
| 489 | |||
| 490 | fn addAbbrev(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void { | ||
| 491 | try store.ensureUnusedCapacity(allocator, 1); | ||
| 492 | store.addAbbrevAssumeCapacity(abbrev); | ||
| 493 | } | ||
| 494 | |||
| 495 | fn addAbbrevAssumeCapacity(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void { | ||
| 496 | store.abbrevs.appendAssumeCapacity(.{ | ||
| 497 | .operands = try allocator.dupe(Abbrev.Operand, abbrev.operands), | ||
| 498 | }); | ||
| 499 | } | ||
| 500 | |||
| 501 | fn addOwnedAbbrev(store: *Store, allocator: std.mem.Allocator, abbrev: Abbrev) !void { | ||
| 502 | try store.abbrevs.ensureUnusedCapacity(allocator, 1); | ||
| 503 | store.addOwnedAbbrevAssumeCapacity(abbrev); | ||
| 504 | } | ||
| 505 | |||
| 506 | fn addOwnedAbbrevAssumeCapacity(store: *Store, abbrev: Abbrev) void { | ||
| 507 | store.abbrevs.appendAssumeCapacity(abbrev); | ||
| 508 | } | ||
| 509 | }; | ||
| 510 | }; | ||
| 511 | |||
| 512 | const assert = std.debug.assert; | ||
| 513 | const std = @import("std"); | ||
| 514 | |||
| 515 | const BitcodeReader = @This(); | ||
stage1/wasi.c+43-1| ... | @@ -178,6 +178,12 @@ struct wasi_ciovec { | ... | @@ -178,6 +178,12 @@ struct wasi_ciovec { |
| 178 | uint32_t len; | 178 | uint32_t len; |
| 179 | }; | 179 | }; |
| 180 | 180 | ||
| 181 | enum wasi_whence { | ||
| 182 | wasi_whence_set = 0, | ||
| 183 | wasi_whence_cur = 1, | ||
| 184 | wasi_whence_end = 2, | ||
| 185 | }; | ||
| 186 | |||
| 181 | extern uint8_t **const wasm_memory; | 187 | extern uint8_t **const wasm_memory; |
| 182 | extern void wasm__start(void); | 188 | extern void wasm__start(void); |
| 183 | 189 | ||
| ... | @@ -946,6 +952,43 @@ uint32_t wasi_snapshot_preview1_fd_pread(uint32_t fd, uint32_t iovs, uint32_t io | ... | @@ -946,6 +952,43 @@ uint32_t wasi_snapshot_preview1_fd_pread(uint32_t fd, uint32_t iovs, uint32_t io |
| 946 | return wasi_errno_success; | 952 | return wasi_errno_success; |
| 947 | } | 953 | } |
| 948 | 954 | ||
| 955 | uint32_t wasi_snapshot_preview1_fd_seek(uint32_t fd, uint64_t in_offset, uint32_t whence, uint32_t res_filesize) { | ||
| 956 | uint8_t *const m = *wasm_memory; | ||
| 957 | int64_t offset = (int64_t)in_offset; | ||
| 958 | uint64_t *res_filesize_ptr = (uint64_t *)&m[res_filesize]; | ||
| 959 | #if LOG_TRACE | ||
| 960 | fprintf(stderr, "wasi_snapshot_preview1_fd_seek(%u, 0x%lld, %u)\n", fd, (long long)offset, whence); | ||
| 961 | #endif | ||
| 962 | |||
| 963 | if (fd >= fd_len || fds[fd].de >= de_len) return wasi_errno_badf; | ||
| 964 | switch (des[fds[fd].de].filetype) { | ||
| 965 | case wasi_filetype_character_device: break; | ||
| 966 | case wasi_filetype_regular_file: break; | ||
| 967 | case wasi_filetype_directory: return wasi_errno_inval; | ||
| 968 | default: panic("unimplemented"); | ||
| 969 | } | ||
| 970 | |||
| 971 | int seek_whence; | ||
| 972 | switch (whence) { | ||
| 973 | case wasi_whence_set: | ||
| 974 | seek_whence = SEEK_SET; | ||
| 975 | break; | ||
| 976 | case wasi_whence_cur: | ||
| 977 | seek_whence = SEEK_CUR; | ||
| 978 | break; | ||
| 979 | case wasi_whence_end: | ||
| 980 | seek_whence = SEEK_END; | ||
| 981 | break; | ||
| 982 | default: | ||
| 983 | return wasi_errno_inval; | ||
| 984 | } | ||
| 985 | if (fseek(fds[fd].stream, offset, seek_whence) < 0) return wasi_errno_io; | ||
| 986 | long res_offset = ftell(fds[fd].stream); | ||
| 987 | if (res_offset < 0) return wasi_errno_io; | ||
| 988 | *res_filesize_ptr = (uint64_t)res_offset; | ||
| 989 | return wasi_errno_success; | ||
| 990 | } | ||
| 991 | |||
| 949 | uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t nsubscriptions, uint32_t res_nevents) { | 992 | uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t nsubscriptions, uint32_t res_nevents) { |
| 950 | (void)in; | 993 | (void)in; |
| 951 | (void)out; | 994 | (void)out; |
| ... | @@ -959,7 +1002,6 @@ uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t | ... | @@ -959,7 +1002,6 @@ uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t |
| 959 | return wasi_errno_success; | 1002 | return wasi_errno_success; |
| 960 | } | 1003 | } |
| 961 | 1004 | ||
| 962 | |||
| 963 | void wasi_snapshot_preview1_debug(uint32_t string, uint64_t x) { | 1005 | void wasi_snapshot_preview1_debug(uint32_t string, uint64_t x) { |
| 964 | uint8_t *const m = *wasm_memory; | 1006 | uint8_t *const m = *wasm_memory; |
| 965 | const char *string_ptr = (const char *)&m[string]; | 1007 | const char *string_ptr = (const char *)&m[string]; |