| ... | ... | @@ -26,6 +26,7 @@ const TypeDescriptor = extern struct { |
| 26 | 26 | signed: bool, |
| 27 | 27 | bit_width: u15, |
| 28 | 28 | }, |
| 29 | float: u16, |
| 29 | 30 | }; |
| 30 | 31 | |
| 31 | 32 | fn getIntegerSize(desc: TypeDescriptor) u64 { |
| ... | ... | @@ -80,10 +81,25 @@ const Value = extern struct { |
| 80 | 81 | return switch (size) { |
| 81 | 82 | 64 => @as(*const i64, @alignCast(@ptrCast(value.handle))).*, |
| 82 | 83 | 128 => @as(*const i128, @alignCast(@ptrCast(value.handle))).*, |
| 83 | | else => unreachable, |
| 84 | else => @trap(), |
| 84 | 85 | }; |
| 85 | 86 | } |
| 86 | 87 | |
| 88 | fn getFloat(value: Value) c_longdouble { |
| 89 | assert(value.type_descriptor.kind == .float); |
| 90 | const size = value.type_descriptor.info.float; |
| 91 | const max_inline_size = @bitSizeOf(ValueHandle); |
| 92 | if (size <= max_inline_size) { |
| 93 | return @bitCast(@intFromPtr(value.handle)); |
| 94 | } |
| 95 | return @floatCast(switch (size) { |
| 96 | 64 => @as(*const f64, @alignCast(@ptrCast(value.handle))).*, |
| 97 | 80 => @as(*const f80, @alignCast(@ptrCast(value.handle))).*, |
| 98 | 128 => @as(*const f128, @alignCast(@ptrCast(value.handle))).*, |
| 99 | else => @trap(), |
| 100 | }); |
| 101 | } |
| 102 | |
| 87 | 103 | fn isMinusOne(value: Value) bool { |
| 88 | 104 | return value.type_descriptor.isSigned() and |
| 89 | 105 | value.getSignedInteger() == -1; |
| ... | ... | @@ -120,7 +136,7 @@ const Value = extern struct { |
| 120 | 136 | try writer.print("{}", .{value.getUnsignedInteger()}); |
| 121 | 137 | } |
| 122 | 138 | }, |
| 123 | | .float => @panic("TODO: write float"), |
| 139 | .float => try writer.print("{}", .{value.getFloat()}), |
| 124 | 140 | .unknown => try writer.writeAll("(unknown)"), |
| 125 | 141 | } |
| 126 | 142 | } |
| ... | ... | @@ -176,11 +192,10 @@ fn divRemHandler( |
| 176 | 192 | lhs_handle: ValueHandle, |
| 177 | 193 | rhs_handle: ValueHandle, |
| 178 | 194 | ) callconv(.c) noreturn { |
| 179 | | const is_signed = data.type_descriptor.isSigned(); |
| 180 | 195 | const lhs = lhs_handle.getValue(data); |
| 181 | 196 | const rhs = rhs_handle.getValue(data); |
| 182 | 197 | |
| 183 | | if (is_signed and rhs.getSignedInteger() == -1) { |
| 198 | if (rhs.isMinusOne()) { |
| 184 | 199 | logMessage( |
| 185 | 200 | "division of {} by -1 cannot be represented in type {s}", |
| 186 | 201 | .{ lhs, data.type_descriptor.getName() }, |
| ... | ... | @@ -409,6 +424,68 @@ fn loadInvalidValue( |
| 409 | 424 | }); |
| 410 | 425 | } |
| 411 | 426 | |
| 427 | const InvalidBuiltinData = extern struct { |
| 428 | loc: SourceLocation, |
| 429 | kind: enum(u8) { |
| 430 | ctz, |
| 431 | clz, |
| 432 | }, |
| 433 | }; |
| 434 | |
| 435 | fn invalidBuiltin(data: *const InvalidBuiltinData) callconv(.c) noreturn { |
| 436 | logMessage( |
| 437 | "passing zero to {s}(), which is not a valid argument", |
| 438 | .{@tagName(data.kind)}, |
| 439 | ); |
| 440 | } |
| 441 | |
| 442 | const VlaBoundNotPositive = extern struct { |
| 443 | loc: SourceLocation, |
| 444 | type_descriptor: *const TypeDescriptor, |
| 445 | }; |
| 446 | |
| 447 | fn vlaBoundNotPositive( |
| 448 | data: *const VlaBoundNotPositive, |
| 449 | bound_handle: ValueHandle, |
| 450 | ) callconv(.c) noreturn { |
| 451 | logMessage("variable length array bound evaluates to non-positive value {}", .{ |
| 452 | bound_handle.getValue(data), |
| 453 | }); |
| 454 | } |
| 455 | |
| 456 | const FloatCastOverflowData = extern struct { |
| 457 | from: *const TypeDescriptor, |
| 458 | to: *const TypeDescriptor, |
| 459 | }; |
| 460 | |
| 461 | const FloatCastOverflowDataV2 = extern struct { |
| 462 | loc: SourceLocation, |
| 463 | from: *const TypeDescriptor, |
| 464 | to: *const TypeDescriptor, |
| 465 | }; |
| 466 | |
| 467 | fn floatCastOverflow( |
| 468 | data_handle: *align(8) const anyopaque, |
| 469 | from_handle: ValueHandle, |
| 470 | ) callconv(.c) noreturn { |
| 471 | // See: https://github.com/llvm/llvm-project/blob/release/19.x/compiler-rt/lib/ubsan/ubsan_handlers.cpp#L463 |
| 472 | // for more information on this check. |
| 473 | const ptr: [*]const u8 = @ptrCast(data_handle); |
| 474 | if (ptr[0] + ptr[1] < 2 or ptr[0] == 0xFF or ptr[1] == 0xFF) { |
| 475 | const data: *const FloatCastOverflowData = @ptrCast(data_handle); |
| 476 | const from_value: Value = .{ .handle = from_handle, .type_descriptor = data.from }; |
| 477 | logMessage("{} is outside the range of representable values of type {s}", .{ |
| 478 | from_value, data.to.getName(), |
| 479 | }); |
| 480 | } else { |
| 481 | const data: *const FloatCastOverflowDataV2 = @ptrCast(data_handle); |
| 482 | const from_value: Value = .{ .handle = from_handle, .type_descriptor = data.from }; |
| 483 | logMessage("{} is outside the range of representable values of type {s}", .{ |
| 484 | from_value, data.to.getName(), |
| 485 | }); |
| 486 | } |
| 487 | } |
| 488 | |
| 412 | 489 | fn SimpleHandler(comptime error_name: []const u8) type { |
| 413 | 490 | return struct { |
| 414 | 491 | fn handler() callconv(.c) noreturn { |
| ... | ... | @@ -479,10 +556,10 @@ comptime { |
| 479 | 556 | exportHandler(&nonNullReturn, "nonnull_return_v1", true); |
| 480 | 557 | exportHandler(&nonNullArg, "nonnull_arg", true); |
| 481 | 558 | exportHandler(&loadInvalidValue, "load_invalid_value", true); |
| 559 | exportHandler(&invalidBuiltin, "invalid_builtin", true); |
| 560 | exportHandler(&vlaBoundNotPositive, "vla_bound_not_positive", true); |
| 561 | exportHandler(&floatCastOverflow, "float_cast_overflow", true); |
| 482 | 562 | |
| 483 | | exportHelper("vla-bound-not-positive", "vla_bound_not_positive", true); |
| 484 | | exportHelper("float-cast-overflow", "float_cast_overflow", true); |
| 485 | | exportHelper("invalid-builtin", "invalid_builtin", true); |
| 486 | 563 | exportHelper("function-type-mismatch", "function_type_mismatch", true); |
| 487 | 564 | exportHelper("implicit-conversion", "implicit_conversion", true); |
| 488 | 565 | exportHelper("nullability-arg", "nullability_arg", true); |