| ... | @@ -26,6 +26,7 @@ const TypeDescriptor = extern struct { | ... | @@ -26,6 +26,7 @@ const TypeDescriptor = extern struct { |
| 26 | signed: bool, | 26 | signed: bool, |
| 27 | bit_width: u15, | 27 | bit_width: u15, |
| 28 | }, | 28 | }, |
| | 29 | float: u16, |
| 29 | }; | 30 | }; |
| 30 | | 31 | |
| 31 | fn getIntegerSize(desc: TypeDescriptor) u64 { | 32 | fn getIntegerSize(desc: TypeDescriptor) u64 { |
| ... | @@ -80,10 +81,25 @@ const Value = extern struct { | ... | @@ -80,10 +81,25 @@ const Value = extern struct { |
| 80 | return switch (size) { | 81 | return switch (size) { |
| 81 | 64 => @as(*const i64, @alignCast(@ptrCast(value.handle))).*, | 82 | 64 => @as(*const i64, @alignCast(@ptrCast(value.handle))).*, |
| 82 | 128 => @as(*const i128, @alignCast(@ptrCast(value.handle))).*, | 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 | fn isMinusOne(value: Value) bool { | 103 | fn isMinusOne(value: Value) bool { |
| 88 | return value.type_descriptor.isSigned() and | 104 | return value.type_descriptor.isSigned() and |
| 89 | value.getSignedInteger() == -1; | 105 | value.getSignedInteger() == -1; |
| ... | @@ -120,7 +136,7 @@ const Value = extern struct { | ... | @@ -120,7 +136,7 @@ const Value = extern struct { |
| 120 | try writer.print("{}", .{value.getUnsignedInteger()}); | 136 | try writer.print("{}", .{value.getUnsignedInteger()}); |
| 121 | } | 137 | } |
| 122 | }, | 138 | }, |
| 123 | .float => @panic("TODO: write float"), | 139 | .float => try writer.print("{}", .{value.getFloat()}), |
| 124 | .unknown => try writer.writeAll("(unknown)"), | 140 | .unknown => try writer.writeAll("(unknown)"), |
| 125 | } | 141 | } |
| 126 | } | 142 | } |
| ... | @@ -176,11 +192,10 @@ fn divRemHandler( | ... | @@ -176,11 +192,10 @@ fn divRemHandler( |
| 176 | lhs_handle: ValueHandle, | 192 | lhs_handle: ValueHandle, |
| 177 | rhs_handle: ValueHandle, | 193 | rhs_handle: ValueHandle, |
| 178 | ) callconv(.c) noreturn { | 194 | ) callconv(.c) noreturn { |
| 179 | const is_signed = data.type_descriptor.isSigned(); | | |
| 180 | const lhs = lhs_handle.getValue(data); | 195 | const lhs = lhs_handle.getValue(data); |
| 181 | const rhs = rhs_handle.getValue(data); | 196 | const rhs = rhs_handle.getValue(data); |
| 182 | | 197 | |
| 183 | if (is_signed and rhs.getSignedInteger() == -1) { | 198 | if (rhs.isMinusOne()) { |
| 184 | logMessage( | 199 | logMessage( |
| 185 | "division of {} by -1 cannot be represented in type {s}", | 200 | "division of {} by -1 cannot be represented in type {s}", |
| 186 | .{ lhs, data.type_descriptor.getName() }, | 201 | .{ lhs, data.type_descriptor.getName() }, |
| ... | @@ -409,6 +424,68 @@ fn loadInvalidValue( | ... | @@ -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 | fn SimpleHandler(comptime error_name: []const u8) type { | 489 | fn SimpleHandler(comptime error_name: []const u8) type { |
| 413 | return struct { | 490 | return struct { |
| 414 | fn handler() callconv(.c) noreturn { | 491 | fn handler() callconv(.c) noreturn { |
| ... | @@ -479,10 +556,10 @@ comptime { | ... | @@ -479,10 +556,10 @@ comptime { |
| 479 | exportHandler(&nonNullReturn, "nonnull_return_v1", true); | 556 | exportHandler(&nonNullReturn, "nonnull_return_v1", true); |
| 480 | exportHandler(&nonNullArg, "nonnull_arg", true); | 557 | exportHandler(&nonNullArg, "nonnull_arg", true); |
| 481 | exportHandler(&loadInvalidValue, "load_invalid_value", true); | 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 | exportHelper("function-type-mismatch", "function_type_mismatch", true); | 563 | exportHelper("function-type-mismatch", "function_type_mismatch", true); |
| 487 | exportHelper("implicit-conversion", "implicit_conversion", true); | 564 | exportHelper("implicit-conversion", "implicit_conversion", true); |
| 488 | exportHelper("nullability-arg", "nullability_arg", true); | 565 | exportHelper("nullability-arg", "nullability_arg", true); |