| author | |
| committer | |
| log | 29b488245daa9210ed9b5e1ffb7290024677f0db |
| tree | 7bcee7504f7caa5f72c8d83bbf066aa494306a70 |
| parent | 051ee8e626111445c27d6c868cb0cdec6df7409e |
* skip installing std/rand_test.zig as it's not needed beyond running
the std lib tests
* add std.math.floor function
* add setFloatMode builtin function to choose between
builtin.FloatMode.Optimized (default) and builtin.FloatMode.Strict
(Optimized is equivalent to -ffast-math in gcc)8 files changed, 328 insertions(+), 24 deletions(-)
CMakeLists.txt-1| ... | ... | @@ -232,7 +232,6 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_x86_64.zig" DESTINATION "${ZIG_S |
| 232 | 232 | install(FILES "${CMAKE_SOURCE_DIR}/std/os/path.zig" DESTINATION "${ZIG_STD_DEST}/os") |
| 233 | 233 | install(FILES "${CMAKE_SOURCE_DIR}/std/os/windows.zig" DESTINATION "${ZIG_STD_DEST}/os") |
| 234 | 234 | install(FILES "${CMAKE_SOURCE_DIR}/std/rand.zig" DESTINATION "${ZIG_STD_DEST}") |
| 235 | install(FILES "${CMAKE_SOURCE_DIR}/std/rand_test.zig" DESTINATION "${ZIG_STD_DEST}") | |
| 236 | 235 | install(FILES "${CMAKE_SOURCE_DIR}/std/sort.zig" DESTINATION "${ZIG_STD_DEST}") |
| 237 | 236 | install(FILES "${CMAKE_SOURCE_DIR}/std/special/bootstrap.zig" DESTINATION "${ZIG_STD_DEST}/special") |
| 238 | 237 | install(FILES "${CMAKE_SOURCE_DIR}/std/special/build_file_template.zig" DESTINATION "${ZIG_STD_DEST}/special") |
src/all_types.hpp+18| ... | ... | @@ -1193,6 +1193,7 @@ enum BuiltinFnId { |
| 1193 | 1193 | BuiltinFnIdTruncate, |
| 1194 | 1194 | BuiltinFnIdIntType, |
| 1195 | 1195 | BuiltinFnIdSetDebugSafety, |
| 1196 | BuiltinFnIdSetFloatMode, | |
| 1196 | 1197 | BuiltinFnIdTypeName, |
| 1197 | 1198 | BuiltinFnIdCanImplicitCast, |
| 1198 | 1199 | BuiltinFnIdSetGlobalAlign, |
| ... | ... | @@ -1580,6 +1581,8 @@ struct ScopeDecls { |
| 1580 | 1581 | HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> decl_table; |
| 1581 | 1582 | bool safety_off; |
| 1582 | 1583 | AstNode *safety_set_node; |
| 1584 | bool fast_math_off; | |
| 1585 | AstNode *fast_math_set_node; | |
| 1583 | 1586 | ImportTableEntry *import; |
| 1584 | 1587 | // If this is a scope from a container, this is the type entry, otherwise null |
| 1585 | 1588 | TypeTableEntry *container_type; |
| ... | ... | @@ -1593,6 +1596,8 @@ struct ScopeBlock { |
| 1593 | 1596 | HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table; |
| 1594 | 1597 | bool safety_off; |
| 1595 | 1598 | AstNode *safety_set_node; |
| 1599 | bool fast_math_off; | |
| 1600 | AstNode *fast_math_set_node; | |
| 1596 | 1601 | }; |
| 1597 | 1602 | |
| 1598 | 1603 | // This scope is created from every defer expression. |
| ... | ... | @@ -1720,6 +1725,7 @@ enum IrInstructionId { |
| 1720 | 1725 | IrInstructionIdToPtrType, |
| 1721 | 1726 | IrInstructionIdPtrTypeChild, |
| 1722 | 1727 | IrInstructionIdSetDebugSafety, |
| 1728 | IrInstructionIdSetFloatMode, | |
| 1723 | 1729 | IrInstructionIdArrayType, |
| 1724 | 1730 | IrInstructionIdSliceType, |
| 1725 | 1731 | IrInstructionIdAsm, |
| ... | ... | @@ -2078,6 +2084,13 @@ struct IrInstructionSetDebugSafety { |
| 2078 | 2084 | IrInstruction *debug_safety_on; |
| 2079 | 2085 | }; |
| 2080 | 2086 | |
| 2087 | struct IrInstructionSetFloatMode { | |
| 2088 | IrInstruction base; | |
| 2089 | ||
| 2090 | IrInstruction *scope_value; | |
| 2091 | IrInstruction *mode_value; | |
| 2092 | }; | |
| 2093 | ||
| 2081 | 2094 | struct IrInstructionArrayType { |
| 2082 | 2095 | IrInstruction base; |
| 2083 | 2096 | |
| ... | ... | @@ -2550,4 +2563,9 @@ static const size_t enum_gen_union_index = 1; |
| 2550 | 2563 | static const size_t err_union_err_index = 0; |
| 2551 | 2564 | static const size_t err_union_payload_index = 1; |
| 2552 | 2565 | |
| 2566 | enum FloatMode { | |
| 2567 | FloatModeStrict, | |
| 2568 | FloatModeOptimized, | |
| 2569 | }; | |
| 2570 | ||
| 2553 | 2571 | #endif |
src/codegen.cpp+55-11| ... | ... | @@ -581,6 +581,24 @@ static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntr |
| 581 | 581 | } |
| 582 | 582 | } |
| 583 | 583 | |
| 584 | static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) { | |
| 585 | // TODO memoize | |
| 586 | Scope *scope = instruction->scope; | |
| 587 | while (scope) { | |
| 588 | if (scope->id == ScopeIdBlock) { | |
| 589 | ScopeBlock *block_scope = (ScopeBlock *)scope; | |
| 590 | if (block_scope->fast_math_set_node) | |
| 591 | return !block_scope->fast_math_off; | |
| 592 | } else if (scope->id == ScopeIdDecls) { | |
| 593 | ScopeDecls *decls_scope = (ScopeDecls *)scope; | |
| 594 | if (decls_scope->fast_math_set_node) | |
| 595 | return !decls_scope->fast_math_off; | |
| 596 | } | |
| 597 | scope = scope->parent; | |
| 598 | } | |
| 599 | return true; | |
| 600 | } | |
| 601 | ||
| 584 | 602 | static bool ir_want_debug_safety(CodeGen *g, IrInstruction *instruction) { |
| 585 | 603 | if (g->build_mode == BuildModeFastRelease) |
| 586 | 604 | return false; |
| ... | ... | @@ -1151,9 +1169,12 @@ enum DivKind { |
| 1151 | 1169 | DivKindExact, |
| 1152 | 1170 | }; |
| 1153 | 1171 | |
| 1154 | static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, LLVMValueRef val1, LLVMValueRef val2, | |
| 1172 | static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, bool want_fast_math, | |
| 1173 | LLVMValueRef val1, LLVMValueRef val2, | |
| 1155 | 1174 | TypeTableEntry *type_entry, DivKind div_kind) |
| 1156 | 1175 | { |
| 1176 | ZigLLVMSetFastMath(g->builder, want_fast_math); | |
| 1177 | ||
| 1157 | 1178 | LLVMValueRef zero = LLVMConstNull(type_entry->type_ref); |
| 1158 | 1179 | if (want_debug_safety) { |
| 1159 | 1180 | LLVMValueRef is_zero_bit; |
| ... | ... | @@ -1287,9 +1308,12 @@ enum RemKind { |
| 1287 | 1308 | RemKindMod, |
| 1288 | 1309 | }; |
| 1289 | 1310 | |
| 1290 | static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, LLVMValueRef val1, LLVMValueRef val2, | |
| 1311 | static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, bool want_fast_math, | |
| 1312 | LLVMValueRef val1, LLVMValueRef val2, | |
| 1291 | 1313 | TypeTableEntry *type_entry, RemKind rem_kind) |
| 1292 | 1314 | { |
| 1315 | ZigLLVMSetFastMath(g->builder, want_fast_math); | |
| 1316 | ||
| 1293 | 1317 | LLVMValueRef zero = LLVMConstNull(type_entry->type_ref); |
| 1294 | 1318 | if (want_debug_safety) { |
| 1295 | 1319 | LLVMValueRef is_zero_bit; |
| ... | ... | @@ -1372,6 +1396,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 1372 | 1396 | case IrBinOpCmpLessOrEq: |
| 1373 | 1397 | case IrBinOpCmpGreaterOrEq: |
| 1374 | 1398 | if (type_entry->id == TypeTableEntryIdFloat) { |
| 1399 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); | |
| 1375 | 1400 | LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id); |
| 1376 | 1401 | return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, ""); |
| 1377 | 1402 | } else if (type_entry->id == TypeTableEntryIdInt) { |
| ... | ... | @@ -1396,6 +1421,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 1396 | 1421 | case IrBinOpAdd: |
| 1397 | 1422 | case IrBinOpAddWrap: |
| 1398 | 1423 | if (type_entry->id == TypeTableEntryIdFloat) { |
| 1424 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); | |
| 1399 | 1425 | return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); |
| 1400 | 1426 | } else if (type_entry->id == TypeTableEntryIdInt) { |
| 1401 | 1427 | bool is_wrapping = (op_id == IrBinOpAddWrap); |
| ... | ... | @@ -1442,6 +1468,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 1442 | 1468 | case IrBinOpSub: |
| 1443 | 1469 | case IrBinOpSubWrap: |
| 1444 | 1470 | if (type_entry->id == TypeTableEntryIdFloat) { |
| 1471 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); | |
| 1445 | 1472 | return LLVMBuildFSub(g->builder, op1_value, op2_value, ""); |
| 1446 | 1473 | } else if (type_entry->id == TypeTableEntryIdInt) { |
| 1447 | 1474 | bool is_wrapping = (op_id == IrBinOpSubWrap); |
| ... | ... | @@ -1460,6 +1487,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 1460 | 1487 | case IrBinOpMult: |
| 1461 | 1488 | case IrBinOpMultWrap: |
| 1462 | 1489 | if (type_entry->id == TypeTableEntryIdFloat) { |
| 1490 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); | |
| 1463 | 1491 | return LLVMBuildFMul(g->builder, op1_value, op2_value, ""); |
| 1464 | 1492 | } else if (type_entry->id == TypeTableEntryIdInt) { |
| 1465 | 1493 | bool is_wrapping = (op_id == IrBinOpMultWrap); |
| ... | ... | @@ -1476,17 +1504,23 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 1476 | 1504 | zig_unreachable(); |
| 1477 | 1505 | } |
| 1478 | 1506 | case IrBinOpDivUnspecified: |
| 1479 | return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindFloat); | |
| 1507 | return gen_div(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base), | |
| 1508 | op1_value, op2_value, type_entry, DivKindFloat); | |
| 1480 | 1509 | case IrBinOpDivExact: |
| 1481 | return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindExact); | |
| 1510 | return gen_div(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base), | |
| 1511 | op1_value, op2_value, type_entry, DivKindExact); | |
| 1482 | 1512 | case IrBinOpDivTrunc: |
| 1483 | return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindTrunc); | |
| 1513 | return gen_div(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base), | |
| 1514 | op1_value, op2_value, type_entry, DivKindTrunc); | |
| 1484 | 1515 | case IrBinOpDivFloor: |
| 1485 | return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindFloor); | |
| 1516 | return gen_div(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base), | |
| 1517 | op1_value, op2_value, type_entry, DivKindFloor); | |
| 1486 | 1518 | case IrBinOpRemRem: |
| 1487 | return gen_rem(g, want_debug_safety, op1_value, op2_value, type_entry, RemKindRem); | |
| 1519 | return gen_rem(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base), | |
| 1520 | op1_value, op2_value, type_entry, RemKindRem); | |
| 1488 | 1521 | case IrBinOpRemMod: |
| 1489 | return gen_rem(g, want_debug_safety, op1_value, op2_value, type_entry, RemKindMod); | |
| 1522 | return gen_rem(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base), | |
| 1523 | op1_value, op2_value, type_entry, RemKindMod); | |
| 1490 | 1524 | } |
| 1491 | 1525 | zig_unreachable(); |
| 1492 | 1526 | } |
| ... | ... | @@ -1602,6 +1636,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, |
| 1602 | 1636 | } |
| 1603 | 1637 | case CastOpFloatToInt: |
| 1604 | 1638 | assert(wanted_type->id == TypeTableEntryIdInt); |
| 1639 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &cast_instruction->base)); | |
| 1605 | 1640 | if (wanted_type->data.integral.is_signed) { |
| 1606 | 1641 | return LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, ""); |
| 1607 | 1642 | } else { |
| ... | ... | @@ -1774,6 +1809,7 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst |
| 1774 | 1809 | case IrUnOpNegationWrap: |
| 1775 | 1810 | { |
| 1776 | 1811 | if (expr_type->id == TypeTableEntryIdFloat) { |
| 1812 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &un_op_instruction->base)); | |
| 1777 | 1813 | return LLVMBuildFNeg(g->builder, expr, ""); |
| 1778 | 1814 | } else if (expr_type->id == TypeTableEntryIdInt) { |
| 1779 | 1815 | if (op_id == IrUnOpNegationWrap) { |
| ... | ... | @@ -2986,6 +3022,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 2986 | 3022 | case IrInstructionIdPtrTypeChild: |
| 2987 | 3023 | case IrInstructionIdFieldPtr: |
| 2988 | 3024 | case IrInstructionIdSetDebugSafety: |
| 3025 | case IrInstructionIdSetFloatMode: | |
| 2989 | 3026 | case IrInstructionIdArrayType: |
| 2990 | 3027 | case IrInstructionIdSliceType: |
| 2991 | 3028 | case IrInstructionIdSizeOf: |
| ... | ... | @@ -4432,6 +4469,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 4432 | 4469 | create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); |
| 4433 | 4470 | create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); |
| 4434 | 4471 | create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2); |
| 4472 | create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 2); | |
| 4435 | 4473 | create_builtin_fn(g, BuiltinFnIdSetGlobalAlign, "setGlobalAlign", 2); |
| 4436 | 4474 | create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2); |
| 4437 | 4475 | create_builtin_fn(g, BuiltinFnIdSetGlobalLinkage, "setGlobalLinkage", 2); |
| ... | ... | @@ -4588,6 +4626,15 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 4588 | 4626 | } |
| 4589 | 4627 | buf_appendf(contents, "};\n\n"); |
| 4590 | 4628 | } |
| 4629 | { | |
| 4630 | buf_appendf(contents, | |
| 4631 | "pub const FloatMode = enum {\n" | |
| 4632 | " Strict,\n" | |
| 4633 | " Optimized,\n" | |
| 4634 | "};\n\n"); | |
| 4635 | assert(FloatModeStrict == 0); | |
| 4636 | assert(FloatModeOptimized == 1); | |
| 4637 | } | |
| 4591 | 4638 | buf_appendf(contents, "pub const is_big_endian = %s;\n", bool_to_str(g->is_big_endian)); |
| 4592 | 4639 | buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); |
| 4593 | 4640 | buf_appendf(contents, "pub const os = Os.%s;\n", cur_os); |
| ... | ... | @@ -4674,9 +4721,6 @@ static void init(CodeGen *g) { |
| 4674 | 4721 | g->builder = LLVMCreateBuilder(); |
| 4675 | 4722 | g->dbuilder = ZigLLVMCreateDIBuilder(g->module, true); |
| 4676 | 4723 | |
| 4677 | ZigLLVMSetFastMath(g->builder, true); | |
| 4678 | ||
| 4679 | ||
| 4680 | 4724 | Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING); |
| 4681 | 4725 | const char *flags = ""; |
| 4682 | 4726 | unsigned runtime_version = 0; |
src/ir.cpp+136-6| ... | ... | @@ -297,6 +297,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetDebugSafety * |
| 297 | 297 | return IrInstructionIdSetDebugSafety; |
| 298 | 298 | } |
| 299 | 299 | |
| 300 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFloatMode *) { | |
| 301 | return IrInstructionIdSetFloatMode; | |
| 302 | } | |
| 303 | ||
| 300 | 304 | static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) { |
| 301 | 305 | return IrInstructionIdArrayType; |
| 302 | 306 | } |
| ... | ... | @@ -1190,6 +1194,19 @@ static IrInstruction *ir_build_set_debug_safety(IrBuilder *irb, Scope *scope, As |
| 1190 | 1194 | return &instruction->base; |
| 1191 | 1195 | } |
| 1192 | 1196 | |
| 1197 | static IrInstruction *ir_build_set_float_mode(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 1198 | IrInstruction *scope_value, IrInstruction *mode_value) | |
| 1199 | { | |
| 1200 | IrInstructionSetFloatMode *instruction = ir_build_instruction<IrInstructionSetFloatMode>(irb, scope, source_node); | |
| 1201 | instruction->scope_value = scope_value; | |
| 1202 | instruction->mode_value = mode_value; | |
| 1203 | ||
| 1204 | ir_ref_instruction(scope_value, irb->current_basic_block); | |
| 1205 | ir_ref_instruction(mode_value, irb->current_basic_block); | |
| 1206 | ||
| 1207 | return &instruction->base; | |
| 1208 | } | |
| 1209 | ||
| 1193 | 1210 | static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *size, |
| 1194 | 1211 | IrInstruction *child_type) |
| 1195 | 1212 | { |
| ... | ... | @@ -2361,6 +2378,14 @@ static IrInstruction *ir_instruction_setdebugsafety_get_dep(IrInstructionSetDebu |
| 2361 | 2378 | } |
| 2362 | 2379 | } |
| 2363 | 2380 | |
| 2381 | static IrInstruction *ir_instruction_setfloatmode_get_dep(IrInstructionSetFloatMode *instruction, size_t index) { | |
| 2382 | switch (index) { | |
| 2383 | case 0: return instruction->scope_value; | |
| 2384 | case 1: return instruction->mode_value; | |
| 2385 | default: return nullptr; | |
| 2386 | } | |
| 2387 | } | |
| 2388 | ||
| 2364 | 2389 | static IrInstruction *ir_instruction_arraytype_get_dep(IrInstructionArrayType *instruction, size_t index) { |
| 2365 | 2390 | switch (index) { |
| 2366 | 2391 | case 0: return instruction->size; |
| ... | ... | @@ -2897,6 +2922,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 2897 | 2922 | return ir_instruction_ptrtypechild_get_dep((IrInstructionPtrTypeChild *) instruction, index); |
| 2898 | 2923 | case IrInstructionIdSetDebugSafety: |
| 2899 | 2924 | return ir_instruction_setdebugsafety_get_dep((IrInstructionSetDebugSafety *) instruction, index); |
| 2925 | case IrInstructionIdSetFloatMode: | |
| 2926 | return ir_instruction_setfloatmode_get_dep((IrInstructionSetFloatMode *) instruction, index); | |
| 2900 | 2927 | case IrInstructionIdArrayType: |
| 2901 | 2928 | return ir_instruction_arraytype_get_dep((IrInstructionArrayType *) instruction, index); |
| 2902 | 2929 | case IrInstructionIdSliceType: |
| ... | ... | @@ -3841,6 +3868,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3841 | 3868 | |
| 3842 | 3869 | return ir_build_set_debug_safety(irb, scope, node, arg0_value, arg1_value); |
| 3843 | 3870 | } |
| 3871 | case BuiltinFnIdSetFloatMode: | |
| 3872 | { | |
| 3873 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 3874 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | |
| 3875 | if (arg0_value == irb->codegen->invalid_instruction) | |
| 3876 | return arg0_value; | |
| 3877 | ||
| 3878 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 3879 | IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); | |
| 3880 | if (arg1_value == irb->codegen->invalid_instruction) | |
| 3881 | return arg1_value; | |
| 3882 | ||
| 3883 | return ir_build_set_float_mode(irb, scope, node, arg0_value, arg1_value); | |
| 3884 | } | |
| 3844 | 3885 | case BuiltinFnIdSizeof: |
| 3845 | 3886 | { |
| 3846 | 3887 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | ... | @@ -7740,6 +7781,16 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) { |
| 7740 | 7781 | return result; |
| 7741 | 7782 | } |
| 7742 | 7783 | |
| 7784 | static ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { | |
| 7785 | Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name)); | |
| 7786 | resolve_top_level_decl(codegen, tld, false); | |
| 7787 | assert(tld->id == TldIdVar); | |
| 7788 | TldVar *tld_var = (TldVar *)tld; | |
| 7789 | ConstExprValue *var_value = tld_var->var->value; | |
| 7790 | assert(var_value != nullptr); | |
| 7791 | return var_value; | |
| 7792 | } | |
| 7793 | ||
| 7743 | 7794 | static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira, |
| 7744 | 7795 | IrInstructionReturn *return_instruction) |
| 7745 | 7796 | { |
| ... | ... | @@ -10556,7 +10607,7 @@ static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira, |
| 10556 | 10607 | AstNode *source_node = set_debug_safety_instruction->base.source_node; |
| 10557 | 10608 | if (*safety_set_node_ptr) { |
| 10558 | 10609 | ErrorMsg *msg = ir_add_error_node(ira, source_node, |
| 10559 | buf_sprintf("function test attribute set twice")); | |
| 10610 | buf_sprintf("debug safety set twice for same scope")); | |
| 10560 | 10611 | add_error_note(ira->codegen, msg, *safety_set_node_ptr, buf_sprintf("first set here")); |
| 10561 | 10612 | return ira->codegen->builtin_types.entry_invalid; |
| 10562 | 10613 | } |
| ... | ... | @@ -10567,6 +10618,86 @@ static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira, |
| 10567 | 10618 | return ira->codegen->builtin_types.entry_void; |
| 10568 | 10619 | } |
| 10569 | 10620 | |
| 10621 | static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira, | |
| 10622 | IrInstructionSetFloatMode *instruction) | |
| 10623 | { | |
| 10624 | IrInstruction *target_instruction = instruction->scope_value->other; | |
| 10625 | TypeTableEntry *target_type = target_instruction->value.type; | |
| 10626 | if (type_is_invalid(target_type)) | |
| 10627 | return ira->codegen->builtin_types.entry_invalid; | |
| 10628 | ConstExprValue *target_val = ir_resolve_const(ira, target_instruction, UndefBad); | |
| 10629 | if (!target_val) | |
| 10630 | return ira->codegen->builtin_types.entry_invalid; | |
| 10631 | ||
| 10632 | if (ira->new_irb.exec->is_inline) { | |
| 10633 | // ignore setFloatMode when running functions at compile time | |
| 10634 | ir_build_const_from(ira, &instruction->base); | |
| 10635 | return ira->codegen->builtin_types.entry_void; | |
| 10636 | } | |
| 10637 | ||
| 10638 | bool *fast_math_off_ptr; | |
| 10639 | AstNode **fast_math_set_node_ptr; | |
| 10640 | if (target_type->id == TypeTableEntryIdBlock) { | |
| 10641 | ScopeBlock *block_scope = (ScopeBlock *)target_val->data.x_block; | |
| 10642 | fast_math_off_ptr = &block_scope->fast_math_off; | |
| 10643 | fast_math_set_node_ptr = &block_scope->fast_math_set_node; | |
| 10644 | } else if (target_type->id == TypeTableEntryIdFn) { | |
| 10645 | FnTableEntry *target_fn = target_val->data.x_fn.fn_entry; | |
| 10646 | assert(target_fn->def_scope); | |
| 10647 | fast_math_off_ptr = &target_fn->def_scope->fast_math_off; | |
| 10648 | fast_math_set_node_ptr = &target_fn->def_scope->fast_math_set_node; | |
| 10649 | } else if (target_type->id == TypeTableEntryIdMetaType) { | |
| 10650 | ScopeDecls *decls_scope; | |
| 10651 | TypeTableEntry *type_arg = target_val->data.x_type; | |
| 10652 | if (type_arg->id == TypeTableEntryIdStruct) { | |
| 10653 | decls_scope = type_arg->data.structure.decls_scope; | |
| 10654 | } else if (type_arg->id == TypeTableEntryIdEnum) { | |
| 10655 | decls_scope = type_arg->data.enumeration.decls_scope; | |
| 10656 | } else if (type_arg->id == TypeTableEntryIdUnion) { | |
| 10657 | decls_scope = type_arg->data.unionation.decls_scope; | |
| 10658 | } else { | |
| 10659 | ir_add_error_node(ira, target_instruction->source_node, | |
| 10660 | buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&type_arg->name))); | |
| 10661 | return ira->codegen->builtin_types.entry_invalid; | |
| 10662 | } | |
| 10663 | fast_math_off_ptr = &decls_scope->fast_math_off; | |
| 10664 | fast_math_set_node_ptr = &decls_scope->fast_math_set_node; | |
| 10665 | } else { | |
| 10666 | ir_add_error_node(ira, target_instruction->source_node, | |
| 10667 | buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&target_type->name))); | |
| 10668 | return ira->codegen->builtin_types.entry_invalid; | |
| 10669 | } | |
| 10670 | ||
| 10671 | ConstExprValue *float_mode_val = get_builtin_value(ira->codegen, "FloatMode"); | |
| 10672 | assert(float_mode_val->type->id == TypeTableEntryIdMetaType); | |
| 10673 | TypeTableEntry *float_mode_enum_type = float_mode_val->data.x_type; | |
| 10674 | ||
| 10675 | IrInstruction *float_mode_value = instruction->mode_value->other; | |
| 10676 | if (type_is_invalid(float_mode_value->value.type)) | |
| 10677 | return ira->codegen->builtin_types.entry_invalid; | |
| 10678 | IrInstruction *casted_value = ir_implicit_cast(ira, float_mode_value, float_mode_enum_type); | |
| 10679 | if (type_is_invalid(casted_value->value.type)) | |
| 10680 | return ira->codegen->builtin_types.entry_invalid; | |
| 10681 | ConstExprValue *mode_val = ir_resolve_const(ira, casted_value, UndefBad); | |
| 10682 | if (!mode_val) | |
| 10683 | return ira->codegen->builtin_types.entry_invalid; | |
| 10684 | ||
| 10685 | bool want_fast_math = (mode_val->data.x_enum.tag == FloatModeOptimized); | |
| 10686 | ||
| 10687 | AstNode *source_node = instruction->base.source_node; | |
| 10688 | if (*fast_math_set_node_ptr) { | |
| 10689 | ErrorMsg *msg = ir_add_error_node(ira, source_node, | |
| 10690 | buf_sprintf("float mode set twice for same scope")); | |
| 10691 | add_error_note(ira->codegen, msg, *fast_math_set_node_ptr, buf_sprintf("first set here")); | |
| 10692 | return ira->codegen->builtin_types.entry_invalid; | |
| 10693 | } | |
| 10694 | *fast_math_set_node_ptr = source_node; | |
| 10695 | *fast_math_off_ptr = !want_fast_math; | |
| 10696 | ||
| 10697 | ir_build_const_from(ira, &instruction->base); | |
| 10698 | return ira->codegen->builtin_types.entry_void; | |
| 10699 | } | |
| 10700 | ||
| 10570 | 10701 | static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, |
| 10571 | 10702 | IrInstructionSliceType *slice_type_instruction) |
| 10572 | 10703 | { |
| ... | ... | @@ -11864,11 +11995,7 @@ static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira, |
| 11864 | 11995 | if (type_is_invalid(type_entry)) |
| 11865 | 11996 | return ira->codegen->builtin_types.entry_invalid; |
| 11866 | 11997 | |
| 11867 | Tld *tld = ira->codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str("TypeId")); | |
| 11868 | resolve_top_level_decl(ira->codegen, tld, false); | |
| 11869 | assert(tld->id == TldIdVar); | |
| 11870 | TldVar *tld_var = (TldVar *)tld; | |
| 11871 | ConstExprValue *var_value = tld_var->var->value; | |
| 11998 | ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeId"); | |
| 11872 | 11999 | assert(var_value->type->id == TypeTableEntryIdMetaType); |
| 11873 | 12000 | TypeTableEntry *result_type = var_value->data.x_type; |
| 11874 | 12001 | |
| ... | ... | @@ -13271,6 +13398,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 13271 | 13398 | return ir_analyze_instruction_set_global_linkage(ira, (IrInstructionSetGlobalLinkage *)instruction); |
| 13272 | 13399 | case IrInstructionIdSetDebugSafety: |
| 13273 | 13400 | return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction); |
| 13401 | case IrInstructionIdSetFloatMode: | |
| 13402 | return ir_analyze_instruction_set_float_mode(ira, (IrInstructionSetFloatMode *)instruction); | |
| 13274 | 13403 | case IrInstructionIdSliceType: |
| 13275 | 13404 | return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction); |
| 13276 | 13405 | case IrInstructionIdAsm: |
| ... | ... | @@ -13482,6 +13611,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 13482 | 13611 | case IrInstructionIdReturn: |
| 13483 | 13612 | case IrInstructionIdUnreachable: |
| 13484 | 13613 | case IrInstructionIdSetDebugSafety: |
| 13614 | case IrInstructionIdSetFloatMode: | |
| 13485 | 13615 | case IrInstructionIdImport: |
| 13486 | 13616 | case IrInstructionIdCompileErr: |
| 13487 | 13617 | case IrInstructionIdCompileLog: |
src/ir_print.cpp+11| ... | ... | @@ -358,6 +358,14 @@ static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety |
| 358 | 358 | fprintf(irp->f, ")"); |
| 359 | 359 | } |
| 360 | 360 | |
| 361 | static void ir_print_set_float_mode(IrPrint *irp, IrInstructionSetFloatMode *instruction) { | |
| 362 | fprintf(irp->f, "@setFloatMode("); | |
| 363 | ir_print_other_instruction(irp, instruction->scope_value); | |
| 364 | fprintf(irp->f, ", "); | |
| 365 | ir_print_other_instruction(irp, instruction->mode_value); | |
| 366 | fprintf(irp->f, ")"); | |
| 367 | } | |
| 368 | ||
| 361 | 369 | static void ir_print_array_type(IrPrint *irp, IrInstructionArrayType *instruction) { |
| 362 | 370 | fprintf(irp->f, "["); |
| 363 | 371 | ir_print_other_instruction(irp, instruction->size); |
| ... | ... | @@ -965,6 +973,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 965 | 973 | case IrInstructionIdSetDebugSafety: |
| 966 | 974 | ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction); |
| 967 | 975 | break; |
| 976 | case IrInstructionIdSetFloatMode: | |
| 977 | ir_print_set_float_mode(irp, (IrInstructionSetFloatMode *)instruction); | |
| 978 | break; | |
| 968 | 979 | case IrInstructionIdArrayType: |
| 969 | 980 | ir_print_array_type(irp, (IrInstructionArrayType *)instruction); |
| 970 | 981 | break; |
std/math.zig+79-5| ... | ... | @@ -252,18 +252,92 @@ fn testRem() { |
| 252 | 252 | |
| 253 | 253 | fn isNan(comptime T: type, x: T) -> bool { |
| 254 | 254 | assert(@typeId(T) == builtin.TypeId.Float); |
| 255 | const bits = floatBits(x); | |
| 256 | 255 | if (T == f32) { |
| 256 | const bits = bitCast(u32, x); | |
| 257 | 257 | return (bits & 0x7fffffff) > 0x7f800000; |
| 258 | 258 | } else if (T == f64) { |
| 259 | const bits = bitCast(u64, x); | |
| 259 | 260 | return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) << 52); |
| 261 | } else if (T == c_longdouble) { | |
| 262 | @compileError("TODO support isNan for c_longdouble"); | |
| 260 | 263 | } else { |
| 261 | 264 | unreachable; |
| 262 | 265 | } |
| 263 | 266 | } |
| 264 | 267 | |
| 265 | fn floatBits(comptime T: type, x: T) -> @IntType(false, T.bit_count) { | |
| 266 | assert(@typeId(T) == builtin.TypeId.Float); | |
| 267 | const uint = @IntType(false, T.bit_count); | |
| 268 | return *@intToPtr(&const uint, &x); | |
| 268 | // TODO this should be a builtin | |
| 269 | fn bitCast(comptime DestType: type, value: var) -> DestType { | |
| 270 | assert(@sizeOf(DestType) == @sizeOf(@typeOf(value))); | |
| 271 | return *@ptrCast(&const DestType, &value); | |
| 272 | } | |
| 273 | ||
| 274 | pub fn floor(x: var) -> @typeOf(x) { | |
| 275 | switch (@typeOf(x)) { | |
| 276 | f32 => floor_f32(x), | |
| 277 | f64 => floor_f64(x), | |
| 278 | c_longdouble => @compileError("TODO support floor for c_longdouble"), | |
| 279 | else => @compileError("Invalid type for floor: " ++ @typeName(@typeOf(x))), | |
| 280 | } | |
| 281 | } | |
| 282 | ||
| 283 | fn floor_f32(x: f32) -> f32 { | |
| 284 | var i = bitCast(u32, x); | |
| 285 | const e = i32((i >> 23) & 0xff) -% 0x7f; | |
| 286 | if (e >= 23) | |
| 287 | return x; | |
| 288 | if (e >= 0) { | |
| 289 | const m = bitCast(u32, 0x007fffff >> e); | |
| 290 | if ((i & m) == 0) | |
| 291 | return x; | |
| 292 | if (i >> 31 != 0) | |
| 293 | i +%= m; | |
| 294 | i &= ~m; | |
| 295 | } else { | |
| 296 | if (i >> 31 == 0) | |
| 297 | return 0; | |
| 298 | if (i <<% 1 != 0) | |
| 299 | return -1.0; | |
| 300 | } | |
| 301 | return bitCast(f32, i); | |
| 302 | } | |
| 303 | ||
| 304 | fn floor_f64(x: f64) -> f64 { | |
| 305 | const DBL_EPSILON = 2.22044604925031308085e-16; | |
| 306 | const toint = 1.0 / DBL_EPSILON; | |
| 307 | ||
| 308 | var i = bitCast(u64, x); | |
| 309 | const e = (i >> 52) & 0x7ff; | |
| 310 | ||
| 311 | if (e >= 0x3ff +% 52 or x == 0) | |
| 312 | return x; | |
| 313 | // y = int(x) - x, where int(x) is an integer neighbor of x | |
| 314 | const y = { | |
| 315 | @setFloatMode(this, builtin.FloatMode.Strict); | |
| 316 | if (i >> 63 != 0) { | |
| 317 | x - toint + toint - x | |
| 318 | } else { | |
| 319 | x + toint - toint - x | |
| 320 | } | |
| 321 | }; | |
| 322 | // special case because of non-nearest rounding modes | |
| 323 | if (e <= 0x3ff - 1) { | |
| 324 | if (i >> 63 != 0) | |
| 325 | return -1.0; | |
| 326 | return 0.0; | |
| 327 | } | |
| 328 | if (y > 0) | |
| 329 | return x + y - 1; | |
| 330 | return x + y; | |
| 331 | } | |
| 332 | ||
| 333 | test "math.floor" { | |
| 334 | assert(floor(f32(1.234)) == 1.0); | |
| 335 | assert(floor(f32(-1.234)) == -2.0); | |
| 336 | assert(floor(f32(999.0)) == 999.0); | |
| 337 | assert(floor(f32(-999.0)) == -999.0); | |
| 338 | ||
| 339 | assert(floor(f64(1.234)) == 1.0); | |
| 340 | assert(floor(f64(-1.234)) == -2.0); | |
| 341 | assert(floor(f64(999.0)) == 999.0); | |
| 342 | assert(floor(f64(-999.0)) == -999.0); | |
| 269 | 343 | } |
test/cases/eval.zig+11-1| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const assert = @import("std").debug.assert; |
| 2 | const builtin = @import("builtin"); | |
| 2 | 3 | |
| 3 | 4 | test "compileTimeRecursion" { |
| 4 | 5 | assert(some_data.len == 21); |
| ... | ... | @@ -222,7 +223,7 @@ test "comptimeIterateOverFnPtrList" { |
| 222 | 223 | assert(performFn('w', 99) == 99); |
| 223 | 224 | } |
| 224 | 225 | |
| 225 | test "evalSetDebugSafetyAtCompileTime" { | |
| 226 | test "eval @setDebugSafety at compile-time" { | |
| 226 | 227 | const result = comptime fnWithSetDebugSafety(); |
| 227 | 228 | assert(result == 1234); |
| 228 | 229 | } |
| ... | ... | @@ -232,6 +233,15 @@ fn fnWithSetDebugSafety() -> i32{ |
| 232 | 233 | return 1234; |
| 233 | 234 | } |
| 234 | 235 | |
| 236 | test "eval @setFloatMode at compile-time" { | |
| 237 | const result = comptime fnWithFloatMode(); | |
| 238 | assert(result == 1234.0); | |
| 239 | } | |
| 240 | ||
| 241 | fn fnWithFloatMode() -> f32 { | |
| 242 | @setFloatMode(this, builtin.FloatMode.Strict); | |
| 243 | return 1234.0; | |
| 244 | } | |
| 235 | 245 | |
| 236 | 246 | |
| 237 | 247 | const SimpleStruct = struct { |
test/compile_errors.zig+18| ... | ... | @@ -1835,4 +1835,22 @@ pub fn addCases(cases: &tests.CompileErrorContext) { |
| 1835 | 1835 | \\} |
| 1836 | 1836 | , |
| 1837 | 1837 | ".tmp_source.zig:3:20: error: cast from 'u16' to 'u8' truncates bits"); |
| 1838 | ||
| 1839 | cases.add("@setDebugSafety twice for same scope", | |
| 1840 | \\export fn foo() { | |
| 1841 | \\ @setDebugSafety(this, false); | |
| 1842 | \\ @setDebugSafety(this, false); | |
| 1843 | \\} | |
| 1844 | , | |
| 1845 | ".tmp_source.zig:3:5: error: debug safety set twice for same scope", | |
| 1846 | ".tmp_source.zig:2:5: note: first set here"); | |
| 1847 | ||
| 1848 | cases.add("@setFloatMode twice for same scope", | |
| 1849 | \\export fn foo() { | |
| 1850 | \\ @setFloatMode(this, @import("builtin").FloatMode.Optimized); | |
| 1851 | \\ @setFloatMode(this, @import("builtin").FloatMode.Optimized); | |
| 1852 | \\} | |
| 1853 | , | |
| 1854 | ".tmp_source.zig:3:5: error: float mode set twice for same scope", | |
| 1855 | ".tmp_source.zig:2:5: note: first set here"); | |
| 1838 | 1856 | } |