| 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 | 77 | |
| 78 | 78 | /// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator. |
| 79 | 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 | 82 | /// The ErrorBundle memory is owned by the `Win32Resource`, using Compilation's general purpose allocator. |
| 83 | 83 | /// This data is accessed by multiple threads and is protected by `mutex`. |
| ... | ... | @@ -318,15 +318,286 @@ pub const CObject = struct { |
| 318 | 318 | failure_retryable, |
| 319 | 319 | }, |
| 320 | 320 | |
| 321 | pub const ErrorMsg = struct { | |
| 322 | msg: []const u8, | |
| 323 | line: u32, | |
| 324 | column: u32, | |
| 321 | pub const Diag = struct { | |
| 322 | level: u32 = 0, | |
| 323 | category: u32 = 0, | |
| 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 { | |
| 327 | gpa.free(em.msg); | |
| 328 | gpa.destroy(em); | |
| 352 | pub fn count(diag: Diag) u32 { | |
| 353 | var total: u32 = 1; | |
| 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 | 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 | 3097 | |
| 2827 | 3098 | /// This function is temporally single-threaded. |
| 2828 | 3099 | pub fn totalErrorCount(self: *Compilation) u32 { |
| 2829 | var total: usize = self.failed_c_objects.count() + | |
| 3100 | var total: usize = | |
| 2830 | 3101 | self.misc_failures.count() + |
| 2831 | 3102 | @intFromBool(self.alloc_failure_occurred) + |
| 2832 | 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 | 3110 | if (!build_options.only_core_functionality) { |
| 2835 | 3111 | for (self.failed_win32_resources.values()) |errs| { |
| 2836 | 3112 | total += errs.errorMessageCount(); |
| ... | ... | @@ -2911,24 +3187,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { |
| 2911 | 3187 | |
| 2912 | 3188 | { |
| 2913 | 3189 | var it = self.failed_c_objects.iterator(); |
| 2914 | while (it.next()) |entry| { | |
| 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 | } | |
| 3190 | while (it.next()) |entry| try entry.value_ptr.*.addToErrorBundle(&bundle); | |
| 2932 | 3191 | } |
| 2933 | 3192 | |
| 2934 | 3193 | if (!build_options.only_core_functionality) { |
| ... | ... | @@ -4209,19 +4468,9 @@ fn reportRetryableCObjectError( |
| 4209 | 4468 | ) error{OutOfMemory}!void { |
| 4210 | 4469 | c_object.status = .failure_retryable; |
| 4211 | 4470 | |
| 4212 | const c_obj_err_msg = try comp.gpa.create(CObject.ErrorMsg); | |
| 4213 | errdefer comp.gpa.destroy(c_obj_err_msg); | |
| 4214 | const msg = try std.fmt.allocPrint(comp.gpa, "{s}", .{@errorName(err)}); | |
| 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); | |
| 4471 | switch (comp.failCObj(c_object, "{s}", .{@errorName(err)})) { | |
| 4472 | error.AnalysisFail => return, | |
| 4473 | else => |e| return e, | |
| 4225 | 4474 | } |
| 4226 | 4475 | } |
| 4227 | 4476 | |
| ... | ... | @@ -4457,6 +4706,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 4457 | 4706 | // We can't know the digest until we do the C compiler invocation, |
| 4458 | 4707 | // so we need a temporary filename. |
| 4459 | 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 | 4710 | var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); |
| 4461 | 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 | 4720 | |
| 4471 | 4721 | try argv.ensureUnusedCapacity(5); |
| 4472 | 4722 | switch (comp.clang_preprocessor_mode) { |
| 4473 | .no => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-c", "-o", out_obj_path }), | |
| 4474 | .yes => argv.appendSliceAssumeCapacity(&[_][]const u8{ "-E", "-o", out_obj_path }), | |
| 4723 | .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }), | |
| 4724 | .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }), | |
| 4475 | 4725 | .stdout => argv.appendAssumeCapacity("-E"), |
| 4476 | 4726 | } |
| 4477 | 4727 | if (comp.clang_passthrough_mode) { |
| 4478 | 4728 | if (comp.emit_asm != null) { |
| 4479 | 4729 | argv.appendAssumeCapacity("-S"); |
| 4480 | 4730 | } else if (comp.emit_llvm_ir != null) { |
| 4481 | argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" }); | |
| 4731 | argv.appendSliceAssumeCapacity(&.{ "-emit-llvm", "-S" }); | |
| 4482 | 4732 | } else if (comp.emit_llvm_bc != null) { |
| 4483 | 4733 | argv.appendAssumeCapacity("-emit-llvm"); |
| 4484 | 4734 | } |
| 4735 | } else { | |
| 4736 | argv.appendSliceAssumeCapacity(&.{ "--serialize-diagnostics", out_diag_path }); | |
| 4485 | 4737 | } |
| 4486 | 4738 | |
| 4487 | 4739 | if (comp.verbose_cc) { |
| ... | ... | @@ -4524,10 +4776,11 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P |
| 4524 | 4776 | switch (term) { |
| 4525 | 4777 | .Exited => |code| { |
| 4526 | 4778 | if (code != 0) { |
| 4527 | // TODO parse clang stderr and turn it into an error message | |
| 4528 | // and then call failCObjWithOwnedErrorMsg | |
| 4529 | log.err("clang failed with stderr: {s}", .{stderr}); | |
| 4530 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); | |
| 4779 | const bundle = CObject.Diag.Bundle.parse(comp.gpa, out_diag_path) catch |err| { | |
| 4780 | log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr }); | |
| 4781 | return comp.failCObj(c_object, "clang exited with code {d}", .{code}); | |
| 4782 | }; | |
| 4783 | return comp.failCObjWithOwnedDiagBundle(c_object, bundle); | |
| 4531 | 4784 | } |
| 4532 | 4785 | }, |
| 4533 | 4786 | else => { |
| ... | ... | @@ -5413,37 +5666,45 @@ pub fn addCCArgs( |
| 5413 | 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 | 5675 | @setCold(true); |
| 5418 | const err_msg = blk: { | |
| 5419 | const msg = try std.fmt.allocPrint(comp.gpa, format, args); | |
| 5420 | errdefer comp.gpa.free(msg); | |
| 5421 | const err_msg = try comp.gpa.create(CObject.ErrorMsg); | |
| 5422 | errdefer comp.gpa.destroy(err_msg); | |
| 5423 | err_msg.* = .{ | |
| 5424 | .msg = msg, | |
| 5425 | .line = 0, | |
| 5426 | .column = 0, | |
| 5427 | }; | |
| 5428 | break :blk err_msg; | |
| 5676 | const diag_bundle = blk: { | |
| 5677 | const diag_bundle = try comp.gpa.create(CObject.Diag.Bundle); | |
| 5678 | diag_bundle.* = .{}; | |
| 5679 | errdefer diag_bundle.destroy(comp.gpa); | |
| 5680 | ||
| 5681 | try diag_bundle.file_names.ensureTotalCapacity(comp.gpa, 1); | |
| 5682 | diag_bundle.file_names.putAssumeCapacity(1, try comp.gpa.dupe(u8, c_object.src.src_path)); | |
| 5683 | ||
| 5684 | diag_bundle.diags = try comp.gpa.alloc(CObject.Diag, 1); | |
| 5685 | diag_bundle.diags[0] = .{}; | |
| 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 | 5695 | comp: *Compilation, |
| 5435 | 5696 | c_object: *CObject, |
| 5436 | err_msg: *CObject.ErrorMsg, | |
| 5697 | diag_bundle: *CObject.Diag.Bundle, | |
| 5437 | 5698 | ) SemaError { |
| 5438 | 5699 | @setCold(true); |
| 5439 | 5700 | { |
| 5440 | 5701 | comp.mutex.lock(); |
| 5441 | 5702 | defer comp.mutex.unlock(); |
| 5442 | 5703 | { |
| 5443 | errdefer err_msg.destroy(comp.gpa); | |
| 5704 | errdefer diag_bundle.destroy(comp.gpa); | |
| 5444 | 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 | 5709 | c_object.status = .failure; |
| 5449 | 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 | 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 | 187 | extern uint8_t **const wasm_memory; |
| 182 | 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 | 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 | 992 | uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t nsubscriptions, uint32_t res_nevents) { |
| 950 | 993 | (void)in; |
| 951 | 994 | (void)out; |
| ... | ... | @@ -959,7 +1002,6 @@ uint32_t wasi_snapshot_preview1_poll_oneoff(uint32_t in, uint32_t out, uint32_t |
| 959 | 1002 | return wasi_errno_success; |
| 960 | 1003 | } |
| 961 | 1004 | |
| 962 | ||
| 963 | 1005 | void wasi_snapshot_preview1_debug(uint32_t string, uint64_t x) { |
| 964 | 1006 | uint8_t *const m = *wasm_memory; |
| 965 | 1007 | const char *string_ptr = (const char *)&m[string]; |