mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
add setFloatMode builtin and std.math.floor
* 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)
This commit is contained in:
parent
051ee8e626
commit
29b488245d
@ -232,7 +232,6 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_x86_64.zig" DESTINATION "${ZIG_S
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/path.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/windows.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/rand.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/rand_test.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/sort.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/bootstrap.zig" DESTINATION "${ZIG_STD_DEST}/special")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/build_file_template.zig" DESTINATION "${ZIG_STD_DEST}/special")
|
||||
|
||||
@ -1193,6 +1193,7 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdTruncate,
|
||||
BuiltinFnIdIntType,
|
||||
BuiltinFnIdSetDebugSafety,
|
||||
BuiltinFnIdSetFloatMode,
|
||||
BuiltinFnIdTypeName,
|
||||
BuiltinFnIdCanImplicitCast,
|
||||
BuiltinFnIdSetGlobalAlign,
|
||||
@ -1580,6 +1581,8 @@ struct ScopeDecls {
|
||||
HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> decl_table;
|
||||
bool safety_off;
|
||||
AstNode *safety_set_node;
|
||||
bool fast_math_off;
|
||||
AstNode *fast_math_set_node;
|
||||
ImportTableEntry *import;
|
||||
// If this is a scope from a container, this is the type entry, otherwise null
|
||||
TypeTableEntry *container_type;
|
||||
@ -1593,6 +1596,8 @@ struct ScopeBlock {
|
||||
HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
|
||||
bool safety_off;
|
||||
AstNode *safety_set_node;
|
||||
bool fast_math_off;
|
||||
AstNode *fast_math_set_node;
|
||||
};
|
||||
|
||||
// This scope is created from every defer expression.
|
||||
@ -1720,6 +1725,7 @@ enum IrInstructionId {
|
||||
IrInstructionIdToPtrType,
|
||||
IrInstructionIdPtrTypeChild,
|
||||
IrInstructionIdSetDebugSafety,
|
||||
IrInstructionIdSetFloatMode,
|
||||
IrInstructionIdArrayType,
|
||||
IrInstructionIdSliceType,
|
||||
IrInstructionIdAsm,
|
||||
@ -2078,6 +2084,13 @@ struct IrInstructionSetDebugSafety {
|
||||
IrInstruction *debug_safety_on;
|
||||
};
|
||||
|
||||
struct IrInstructionSetFloatMode {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *scope_value;
|
||||
IrInstruction *mode_value;
|
||||
};
|
||||
|
||||
struct IrInstructionArrayType {
|
||||
IrInstruction base;
|
||||
|
||||
@ -2550,4 +2563,9 @@ static const size_t enum_gen_union_index = 1;
|
||||
static const size_t err_union_err_index = 0;
|
||||
static const size_t err_union_payload_index = 1;
|
||||
|
||||
enum FloatMode {
|
||||
FloatModeStrict,
|
||||
FloatModeOptimized,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -581,6 +581,24 @@ static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, TypeTableEntr
|
||||
}
|
||||
}
|
||||
|
||||
static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) {
|
||||
// TODO memoize
|
||||
Scope *scope = instruction->scope;
|
||||
while (scope) {
|
||||
if (scope->id == ScopeIdBlock) {
|
||||
ScopeBlock *block_scope = (ScopeBlock *)scope;
|
||||
if (block_scope->fast_math_set_node)
|
||||
return !block_scope->fast_math_off;
|
||||
} else if (scope->id == ScopeIdDecls) {
|
||||
ScopeDecls *decls_scope = (ScopeDecls *)scope;
|
||||
if (decls_scope->fast_math_set_node)
|
||||
return !decls_scope->fast_math_off;
|
||||
}
|
||||
scope = scope->parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ir_want_debug_safety(CodeGen *g, IrInstruction *instruction) {
|
||||
if (g->build_mode == BuildModeFastRelease)
|
||||
return false;
|
||||
@ -1151,9 +1169,12 @@ enum DivKind {
|
||||
DivKindExact,
|
||||
};
|
||||
|
||||
static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, LLVMValueRef val1, LLVMValueRef val2,
|
||||
static LLVMValueRef gen_div(CodeGen *g, bool want_debug_safety, bool want_fast_math,
|
||||
LLVMValueRef val1, LLVMValueRef val2,
|
||||
TypeTableEntry *type_entry, DivKind div_kind)
|
||||
{
|
||||
ZigLLVMSetFastMath(g->builder, want_fast_math);
|
||||
|
||||
LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
|
||||
if (want_debug_safety) {
|
||||
LLVMValueRef is_zero_bit;
|
||||
@ -1287,9 +1308,12 @@ enum RemKind {
|
||||
RemKindMod,
|
||||
};
|
||||
|
||||
static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, LLVMValueRef val1, LLVMValueRef val2,
|
||||
static LLVMValueRef gen_rem(CodeGen *g, bool want_debug_safety, bool want_fast_math,
|
||||
LLVMValueRef val1, LLVMValueRef val2,
|
||||
TypeTableEntry *type_entry, RemKind rem_kind)
|
||||
{
|
||||
ZigLLVMSetFastMath(g->builder, want_fast_math);
|
||||
|
||||
LLVMValueRef zero = LLVMConstNull(type_entry->type_ref);
|
||||
if (want_debug_safety) {
|
||||
LLVMValueRef is_zero_bit;
|
||||
@ -1372,6 +1396,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
||||
case IrBinOpCmpLessOrEq:
|
||||
case IrBinOpCmpGreaterOrEq:
|
||||
if (type_entry->id == TypeTableEntryIdFloat) {
|
||||
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
|
||||
LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id);
|
||||
return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, "");
|
||||
} else if (type_entry->id == TypeTableEntryIdInt) {
|
||||
@ -1396,6 +1421,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
||||
case IrBinOpAdd:
|
||||
case IrBinOpAddWrap:
|
||||
if (type_entry->id == TypeTableEntryIdFloat) {
|
||||
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
|
||||
return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
|
||||
} else if (type_entry->id == TypeTableEntryIdInt) {
|
||||
bool is_wrapping = (op_id == IrBinOpAddWrap);
|
||||
@ -1442,6 +1468,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
||||
case IrBinOpSub:
|
||||
case IrBinOpSubWrap:
|
||||
if (type_entry->id == TypeTableEntryIdFloat) {
|
||||
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
|
||||
return LLVMBuildFSub(g->builder, op1_value, op2_value, "");
|
||||
} else if (type_entry->id == TypeTableEntryIdInt) {
|
||||
bool is_wrapping = (op_id == IrBinOpSubWrap);
|
||||
@ -1460,6 +1487,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
||||
case IrBinOpMult:
|
||||
case IrBinOpMultWrap:
|
||||
if (type_entry->id == TypeTableEntryIdFloat) {
|
||||
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
|
||||
return LLVMBuildFMul(g->builder, op1_value, op2_value, "");
|
||||
} else if (type_entry->id == TypeTableEntryIdInt) {
|
||||
bool is_wrapping = (op_id == IrBinOpMultWrap);
|
||||
@ -1476,17 +1504,23 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
||||
zig_unreachable();
|
||||
}
|
||||
case IrBinOpDivUnspecified:
|
||||
return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindFloat);
|
||||
return gen_div(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base),
|
||||
op1_value, op2_value, type_entry, DivKindFloat);
|
||||
case IrBinOpDivExact:
|
||||
return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindExact);
|
||||
return gen_div(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base),
|
||||
op1_value, op2_value, type_entry, DivKindExact);
|
||||
case IrBinOpDivTrunc:
|
||||
return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindTrunc);
|
||||
return gen_div(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base),
|
||||
op1_value, op2_value, type_entry, DivKindTrunc);
|
||||
case IrBinOpDivFloor:
|
||||
return gen_div(g, want_debug_safety, op1_value, op2_value, type_entry, DivKindFloor);
|
||||
return gen_div(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base),
|
||||
op1_value, op2_value, type_entry, DivKindFloor);
|
||||
case IrBinOpRemRem:
|
||||
return gen_rem(g, want_debug_safety, op1_value, op2_value, type_entry, RemKindRem);
|
||||
return gen_rem(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base),
|
||||
op1_value, op2_value, type_entry, RemKindRem);
|
||||
case IrBinOpRemMod:
|
||||
return gen_rem(g, want_debug_safety, op1_value, op2_value, type_entry, RemKindMod);
|
||||
return gen_rem(g, want_debug_safety, ir_want_fast_math(g, &bin_op_instruction->base),
|
||||
op1_value, op2_value, type_entry, RemKindMod);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -1602,6 +1636,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
|
||||
}
|
||||
case CastOpFloatToInt:
|
||||
assert(wanted_type->id == TypeTableEntryIdInt);
|
||||
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &cast_instruction->base));
|
||||
if (wanted_type->data.integral.is_signed) {
|
||||
return LLVMBuildFPToSI(g->builder, expr_val, wanted_type->type_ref, "");
|
||||
} else {
|
||||
@ -1774,6 +1809,7 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst
|
||||
case IrUnOpNegationWrap:
|
||||
{
|
||||
if (expr_type->id == TypeTableEntryIdFloat) {
|
||||
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &un_op_instruction->base));
|
||||
return LLVMBuildFNeg(g->builder, expr, "");
|
||||
} else if (expr_type->id == TypeTableEntryIdInt) {
|
||||
if (op_id == IrUnOpNegationWrap) {
|
||||
@ -2986,6 +3022,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
case IrInstructionIdPtrTypeChild:
|
||||
case IrInstructionIdFieldPtr:
|
||||
case IrInstructionIdSetDebugSafety:
|
||||
case IrInstructionIdSetFloatMode:
|
||||
case IrInstructionIdArrayType:
|
||||
case IrInstructionIdSliceType:
|
||||
case IrInstructionIdSizeOf:
|
||||
@ -4432,6 +4469,7 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
|
||||
create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetGlobalAlign, "setGlobalAlign", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSetGlobalLinkage, "setGlobalLinkage", 2);
|
||||
@ -4588,6 +4626,15 @@ static void define_builtin_compile_vars(CodeGen *g) {
|
||||
}
|
||||
buf_appendf(contents, "};\n\n");
|
||||
}
|
||||
{
|
||||
buf_appendf(contents,
|
||||
"pub const FloatMode = enum {\n"
|
||||
" Strict,\n"
|
||||
" Optimized,\n"
|
||||
"};\n\n");
|
||||
assert(FloatModeStrict == 0);
|
||||
assert(FloatModeOptimized == 1);
|
||||
}
|
||||
buf_appendf(contents, "pub const is_big_endian = %s;\n", bool_to_str(g->is_big_endian));
|
||||
buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build));
|
||||
buf_appendf(contents, "pub const os = Os.%s;\n", cur_os);
|
||||
@ -4674,9 +4721,6 @@ static void init(CodeGen *g) {
|
||||
g->builder = LLVMCreateBuilder();
|
||||
g->dbuilder = ZigLLVMCreateDIBuilder(g->module, true);
|
||||
|
||||
ZigLLVMSetFastMath(g->builder, true);
|
||||
|
||||
|
||||
Buf *producer = buf_sprintf("zig %s", ZIG_VERSION_STRING);
|
||||
const char *flags = "";
|
||||
unsigned runtime_version = 0;
|
||||
|
||||
142
src/ir.cpp
142
src/ir.cpp
@ -297,6 +297,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetDebugSafety *
|
||||
return IrInstructionIdSetDebugSafety;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFloatMode *) {
|
||||
return IrInstructionIdSetFloatMode;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) {
|
||||
return IrInstructionIdArrayType;
|
||||
}
|
||||
@ -1190,6 +1194,19 @@ static IrInstruction *ir_build_set_debug_safety(IrBuilder *irb, Scope *scope, As
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_set_float_mode(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *scope_value, IrInstruction *mode_value)
|
||||
{
|
||||
IrInstructionSetFloatMode *instruction = ir_build_instruction<IrInstructionSetFloatMode>(irb, scope, source_node);
|
||||
instruction->scope_value = scope_value;
|
||||
instruction->mode_value = mode_value;
|
||||
|
||||
ir_ref_instruction(scope_value, irb->current_basic_block);
|
||||
ir_ref_instruction(mode_value, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *size,
|
||||
IrInstruction *child_type)
|
||||
{
|
||||
@ -2361,6 +2378,14 @@ static IrInstruction *ir_instruction_setdebugsafety_get_dep(IrInstructionSetDebu
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_setfloatmode_get_dep(IrInstructionSetFloatMode *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->scope_value;
|
||||
case 1: return instruction->mode_value;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_arraytype_get_dep(IrInstructionArrayType *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->size;
|
||||
@ -2897,6 +2922,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
|
||||
return ir_instruction_ptrtypechild_get_dep((IrInstructionPtrTypeChild *) instruction, index);
|
||||
case IrInstructionIdSetDebugSafety:
|
||||
return ir_instruction_setdebugsafety_get_dep((IrInstructionSetDebugSafety *) instruction, index);
|
||||
case IrInstructionIdSetFloatMode:
|
||||
return ir_instruction_setfloatmode_get_dep((IrInstructionSetFloatMode *) instruction, index);
|
||||
case IrInstructionIdArrayType:
|
||||
return ir_instruction_arraytype_get_dep((IrInstructionArrayType *) instruction, index);
|
||||
case IrInstructionIdSliceType:
|
||||
@ -3841,6 +3868,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
||||
|
||||
return ir_build_set_debug_safety(irb, scope, node, arg0_value, arg1_value);
|
||||
}
|
||||
case BuiltinFnIdSetFloatMode:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
||||
if (arg0_value == irb->codegen->invalid_instruction)
|
||||
return arg0_value;
|
||||
|
||||
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
||||
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
||||
if (arg1_value == irb->codegen->invalid_instruction)
|
||||
return arg1_value;
|
||||
|
||||
return ir_build_set_float_mode(irb, scope, node, arg0_value, arg1_value);
|
||||
}
|
||||
case BuiltinFnIdSizeof:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
@ -7740,6 +7781,16 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) {
|
||||
Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name));
|
||||
resolve_top_level_decl(codegen, tld, false);
|
||||
assert(tld->id == TldIdVar);
|
||||
TldVar *tld_var = (TldVar *)tld;
|
||||
ConstExprValue *var_value = tld_var->var->value;
|
||||
assert(var_value != nullptr);
|
||||
return var_value;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira,
|
||||
IrInstructionReturn *return_instruction)
|
||||
{
|
||||
@ -10556,7 +10607,7 @@ static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,
|
||||
AstNode *source_node = set_debug_safety_instruction->base.source_node;
|
||||
if (*safety_set_node_ptr) {
|
||||
ErrorMsg *msg = ir_add_error_node(ira, source_node,
|
||||
buf_sprintf("function test attribute set twice"));
|
||||
buf_sprintf("debug safety set twice for same scope"));
|
||||
add_error_note(ira->codegen, msg, *safety_set_node_ptr, buf_sprintf("first set here"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
@ -10567,6 +10618,86 @@ static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
|
||||
IrInstructionSetFloatMode *instruction)
|
||||
{
|
||||
IrInstruction *target_instruction = instruction->scope_value->other;
|
||||
TypeTableEntry *target_type = target_instruction->value.type;
|
||||
if (type_is_invalid(target_type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
ConstExprValue *target_val = ir_resolve_const(ira, target_instruction, UndefBad);
|
||||
if (!target_val)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
if (ira->new_irb.exec->is_inline) {
|
||||
// ignore setFloatMode when running functions at compile time
|
||||
ir_build_const_from(ira, &instruction->base);
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
bool *fast_math_off_ptr;
|
||||
AstNode **fast_math_set_node_ptr;
|
||||
if (target_type->id == TypeTableEntryIdBlock) {
|
||||
ScopeBlock *block_scope = (ScopeBlock *)target_val->data.x_block;
|
||||
fast_math_off_ptr = &block_scope->fast_math_off;
|
||||
fast_math_set_node_ptr = &block_scope->fast_math_set_node;
|
||||
} else if (target_type->id == TypeTableEntryIdFn) {
|
||||
FnTableEntry *target_fn = target_val->data.x_fn.fn_entry;
|
||||
assert(target_fn->def_scope);
|
||||
fast_math_off_ptr = &target_fn->def_scope->fast_math_off;
|
||||
fast_math_set_node_ptr = &target_fn->def_scope->fast_math_set_node;
|
||||
} else if (target_type->id == TypeTableEntryIdMetaType) {
|
||||
ScopeDecls *decls_scope;
|
||||
TypeTableEntry *type_arg = target_val->data.x_type;
|
||||
if (type_arg->id == TypeTableEntryIdStruct) {
|
||||
decls_scope = type_arg->data.structure.decls_scope;
|
||||
} else if (type_arg->id == TypeTableEntryIdEnum) {
|
||||
decls_scope = type_arg->data.enumeration.decls_scope;
|
||||
} else if (type_arg->id == TypeTableEntryIdUnion) {
|
||||
decls_scope = type_arg->data.unionation.decls_scope;
|
||||
} else {
|
||||
ir_add_error_node(ira, target_instruction->source_node,
|
||||
buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&type_arg->name)));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
fast_math_off_ptr = &decls_scope->fast_math_off;
|
||||
fast_math_set_node_ptr = &decls_scope->fast_math_set_node;
|
||||
} else {
|
||||
ir_add_error_node(ira, target_instruction->source_node,
|
||||
buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&target_type->name)));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
ConstExprValue *float_mode_val = get_builtin_value(ira->codegen, "FloatMode");
|
||||
assert(float_mode_val->type->id == TypeTableEntryIdMetaType);
|
||||
TypeTableEntry *float_mode_enum_type = float_mode_val->data.x_type;
|
||||
|
||||
IrInstruction *float_mode_value = instruction->mode_value->other;
|
||||
if (type_is_invalid(float_mode_value->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
IrInstruction *casted_value = ir_implicit_cast(ira, float_mode_value, float_mode_enum_type);
|
||||
if (type_is_invalid(casted_value->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
ConstExprValue *mode_val = ir_resolve_const(ira, casted_value, UndefBad);
|
||||
if (!mode_val)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
bool want_fast_math = (mode_val->data.x_enum.tag == FloatModeOptimized);
|
||||
|
||||
AstNode *source_node = instruction->base.source_node;
|
||||
if (*fast_math_set_node_ptr) {
|
||||
ErrorMsg *msg = ir_add_error_node(ira, source_node,
|
||||
buf_sprintf("float mode set twice for same scope"));
|
||||
add_error_note(ira->codegen, msg, *fast_math_set_node_ptr, buf_sprintf("first set here"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
*fast_math_set_node_ptr = source_node;
|
||||
*fast_math_off_ptr = !want_fast_math;
|
||||
|
||||
ir_build_const_from(ira, &instruction->base);
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
|
||||
IrInstructionSliceType *slice_type_instruction)
|
||||
{
|
||||
@ -11864,11 +11995,7 @@ static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira,
|
||||
if (type_is_invalid(type_entry))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
Tld *tld = ira->codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str("TypeId"));
|
||||
resolve_top_level_decl(ira->codegen, tld, false);
|
||||
assert(tld->id == TldIdVar);
|
||||
TldVar *tld_var = (TldVar *)tld;
|
||||
ConstExprValue *var_value = tld_var->var->value;
|
||||
ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeId");
|
||||
assert(var_value->type->id == TypeTableEntryIdMetaType);
|
||||
TypeTableEntry *result_type = var_value->data.x_type;
|
||||
|
||||
@ -13271,6 +13398,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||
return ir_analyze_instruction_set_global_linkage(ira, (IrInstructionSetGlobalLinkage *)instruction);
|
||||
case IrInstructionIdSetDebugSafety:
|
||||
return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction);
|
||||
case IrInstructionIdSetFloatMode:
|
||||
return ir_analyze_instruction_set_float_mode(ira, (IrInstructionSetFloatMode *)instruction);
|
||||
case IrInstructionIdSliceType:
|
||||
return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
|
||||
case IrInstructionIdAsm:
|
||||
@ -13482,6 +13611,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
case IrInstructionIdReturn:
|
||||
case IrInstructionIdUnreachable:
|
||||
case IrInstructionIdSetDebugSafety:
|
||||
case IrInstructionIdSetFloatMode:
|
||||
case IrInstructionIdImport:
|
||||
case IrInstructionIdCompileErr:
|
||||
case IrInstructionIdCompileLog:
|
||||
|
||||
@ -358,6 +358,14 @@ static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_set_float_mode(IrPrint *irp, IrInstructionSetFloatMode *instruction) {
|
||||
fprintf(irp->f, "@setFloatMode(");
|
||||
ir_print_other_instruction(irp, instruction->scope_value);
|
||||
fprintf(irp->f, ", ");
|
||||
ir_print_other_instruction(irp, instruction->mode_value);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_array_type(IrPrint *irp, IrInstructionArrayType *instruction) {
|
||||
fprintf(irp->f, "[");
|
||||
ir_print_other_instruction(irp, instruction->size);
|
||||
@ -965,6 +973,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdSetDebugSafety:
|
||||
ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction);
|
||||
break;
|
||||
case IrInstructionIdSetFloatMode:
|
||||
ir_print_set_float_mode(irp, (IrInstructionSetFloatMode *)instruction);
|
||||
break;
|
||||
case IrInstructionIdArrayType:
|
||||
ir_print_array_type(irp, (IrInstructionArrayType *)instruction);
|
||||
break;
|
||||
|
||||
84
std/math.zig
84
std/math.zig
@ -252,18 +252,92 @@ fn testRem() {
|
||||
|
||||
fn isNan(comptime T: type, x: T) -> bool {
|
||||
assert(@typeId(T) == builtin.TypeId.Float);
|
||||
const bits = floatBits(x);
|
||||
if (T == f32) {
|
||||
const bits = bitCast(u32, x);
|
||||
return (bits & 0x7fffffff) > 0x7f800000;
|
||||
} else if (T == f64) {
|
||||
const bits = bitCast(u64, x);
|
||||
return (bits & (@maxValue(u64) >> 1)) > (u64(0x7ff) << 52);
|
||||
} else if (T == c_longdouble) {
|
||||
@compileError("TODO support isNan for c_longdouble");
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
fn floatBits(comptime T: type, x: T) -> @IntType(false, T.bit_count) {
|
||||
assert(@typeId(T) == builtin.TypeId.Float);
|
||||
const uint = @IntType(false, T.bit_count);
|
||||
return *@intToPtr(&const uint, &x);
|
||||
// TODO this should be a builtin
|
||||
fn bitCast(comptime DestType: type, value: var) -> DestType {
|
||||
assert(@sizeOf(DestType) == @sizeOf(@typeOf(value)));
|
||||
return *@ptrCast(&const DestType, &value);
|
||||
}
|
||||
|
||||
pub fn floor(x: var) -> @typeOf(x) {
|
||||
switch (@typeOf(x)) {
|
||||
f32 => floor_f32(x),
|
||||
f64 => floor_f64(x),
|
||||
c_longdouble => @compileError("TODO support floor for c_longdouble"),
|
||||
else => @compileError("Invalid type for floor: " ++ @typeName(@typeOf(x))),
|
||||
}
|
||||
}
|
||||
|
||||
fn floor_f32(x: f32) -> f32 {
|
||||
var i = bitCast(u32, x);
|
||||
const e = i32((i >> 23) & 0xff) -% 0x7f;
|
||||
if (e >= 23)
|
||||
return x;
|
||||
if (e >= 0) {
|
||||
const m = bitCast(u32, 0x007fffff >> e);
|
||||
if ((i & m) == 0)
|
||||
return x;
|
||||
if (i >> 31 != 0)
|
||||
i +%= m;
|
||||
i &= ~m;
|
||||
} else {
|
||||
if (i >> 31 == 0)
|
||||
return 0;
|
||||
if (i <<% 1 != 0)
|
||||
return -1.0;
|
||||
}
|
||||
return bitCast(f32, i);
|
||||
}
|
||||
|
||||
fn floor_f64(x: f64) -> f64 {
|
||||
const DBL_EPSILON = 2.22044604925031308085e-16;
|
||||
const toint = 1.0 / DBL_EPSILON;
|
||||
|
||||
var i = bitCast(u64, x);
|
||||
const e = (i >> 52) & 0x7ff;
|
||||
|
||||
if (e >= 0x3ff +% 52 or x == 0)
|
||||
return x;
|
||||
// y = int(x) - x, where int(x) is an integer neighbor of x
|
||||
const y = {
|
||||
@setFloatMode(this, builtin.FloatMode.Strict);
|
||||
if (i >> 63 != 0) {
|
||||
x - toint + toint - x
|
||||
} else {
|
||||
x + toint - toint - x
|
||||
}
|
||||
};
|
||||
// special case because of non-nearest rounding modes
|
||||
if (e <= 0x3ff - 1) {
|
||||
if (i >> 63 != 0)
|
||||
return -1.0;
|
||||
return 0.0;
|
||||
}
|
||||
if (y > 0)
|
||||
return x + y - 1;
|
||||
return x + y;
|
||||
}
|
||||
|
||||
test "math.floor" {
|
||||
assert(floor(f32(1.234)) == 1.0);
|
||||
assert(floor(f32(-1.234)) == -2.0);
|
||||
assert(floor(f32(999.0)) == 999.0);
|
||||
assert(floor(f32(-999.0)) == -999.0);
|
||||
|
||||
assert(floor(f64(1.234)) == 1.0);
|
||||
assert(floor(f64(-1.234)) == -2.0);
|
||||
assert(floor(f64(999.0)) == 999.0);
|
||||
assert(floor(f64(-999.0)) == -999.0);
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
test "compileTimeRecursion" {
|
||||
assert(some_data.len == 21);
|
||||
@ -222,7 +223,7 @@ test "comptimeIterateOverFnPtrList" {
|
||||
assert(performFn('w', 99) == 99);
|
||||
}
|
||||
|
||||
test "evalSetDebugSafetyAtCompileTime" {
|
||||
test "eval @setDebugSafety at compile-time" {
|
||||
const result = comptime fnWithSetDebugSafety();
|
||||
assert(result == 1234);
|
||||
}
|
||||
@ -232,6 +233,15 @@ fn fnWithSetDebugSafety() -> i32{
|
||||
return 1234;
|
||||
}
|
||||
|
||||
test "eval @setFloatMode at compile-time" {
|
||||
const result = comptime fnWithFloatMode();
|
||||
assert(result == 1234.0);
|
||||
}
|
||||
|
||||
fn fnWithFloatMode() -> f32 {
|
||||
@setFloatMode(this, builtin.FloatMode.Strict);
|
||||
return 1234.0;
|
||||
}
|
||||
|
||||
|
||||
const SimpleStruct = struct {
|
||||
|
||||
@ -1835,4 +1835,22 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:3:20: error: cast from 'u16' to 'u8' truncates bits");
|
||||
|
||||
cases.add("@setDebugSafety twice for same scope",
|
||||
\\export fn foo() {
|
||||
\\ @setDebugSafety(this, false);
|
||||
\\ @setDebugSafety(this, false);
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:3:5: error: debug safety set twice for same scope",
|
||||
".tmp_source.zig:2:5: note: first set here");
|
||||
|
||||
cases.add("@setFloatMode twice for same scope",
|
||||
\\export fn foo() {
|
||||
\\ @setFloatMode(this, @import("builtin").FloatMode.Optimized);
|
||||
\\ @setFloatMode(this, @import("builtin").FloatMode.Optimized);
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:3:5: error: float mode set twice for same scope",
|
||||
".tmp_source.zig:2:5: note: first set here");
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user