IR: implement ctz and clz builtins

master
Andrew Kelley 2016-11-21 13:27:44 -05:00
parent 71d95c6597
commit 67d565136a
7 changed files with 270 additions and 100 deletions

View File

@ -538,12 +538,12 @@ expression is not known at compile time.
The result of the function is the result of the expression. The result of the function is the result of the expression.
### @ctz(inline T: type, x: T) -> T ### @ctz(x: T) -> T
This function counts the number of trailing zeroes in x which is an integer This function counts the number of trailing zeroes in x which is an integer
type T. type T.
### @clz(inline T: type, x: T) -> T ### @clz(x: T) -> T
This function counts the number of leading zeroes in x which is an integer This function counts the number of leading zeroes in x which is an integer
type T. type T.

View File

@ -1455,6 +1455,8 @@ enum IrInstructionId {
IrInstructionIdSizeOf, IrInstructionIdSizeOf,
IrInstructionIdTestNull, IrInstructionIdTestNull,
IrInstructionIdUnwrapMaybe, IrInstructionIdUnwrapMaybe,
IrInstructionIdClz,
IrInstructionIdCtz,
}; };
struct IrInstruction { struct IrInstruction {
@ -1766,6 +1768,18 @@ struct IrInstructionUnwrapMaybe {
bool safety_check_on; bool safety_check_on;
}; };
struct IrInstructionCtz {
IrInstruction base;
IrInstruction *value;
};
struct IrInstructionClz {
IrInstruction base;
IrInstruction *value;
};
enum LValPurpose { enum LValPurpose {
LValPurposeNone, LValPurposeNone,
LValPurposeAssign, LValPurposeAssign,

View File

@ -360,3 +360,37 @@ bool bignum_multiply_by_scalar(BigNum *bignum, uint64_t scalar) {
assert(!bignum->is_negative); assert(!bignum->is_negative);
return __builtin_umulll_overflow(bignum->data.x_uint, scalar, &bignum->data.x_uint); return __builtin_umulll_overflow(bignum->data.x_uint, scalar, &bignum->data.x_uint);
} }
uint32_t bignum_ctz(BigNum *bignum, uint32_t bit_count) {
assert(bignum->kind == BigNumKindInt);
uint64_t x = bignum_to_twos_complement(bignum);
uint32_t result = 0;
for (uint32_t i = 0; i < bit_count; i += 1) {
if ((x & 0x1) != 0)
break;
result += 1;
x = x >> 1;
}
return result;
}
uint32_t bignum_clz(BigNum *bignum, uint32_t bit_count) {
assert(bignum->kind == BigNumKindInt);
if (bit_count == 0)
return 0;
uint64_t x = bignum_to_twos_complement(bignum);
uint64_t mask = ((uint64_t)1) << ((uint64_t)bit_count - 1);
uint32_t result = 0;
for (uint32_t i = 0; i < bit_count; i += 1) {
if ((x & mask) != 0)
break;
result += 1;
x = x << 1;
}
return result;
}

View File

@ -66,4 +66,7 @@ bool bignum_multiply_by_scalar(BigNum *bignum, uint64_t scalar);
struct Buf; struct Buf;
Buf *bignum_to_buf(BigNum *bn); Buf *bignum_to_buf(BigNum *bn);
uint32_t bignum_ctz(BigNum *bignum, uint32_t bit_count);
uint32_t bignum_clz(BigNum *bignum, uint32_t bit_count);
#endif #endif

View File

@ -1478,6 +1478,46 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
} }
} }
static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, BuiltinFnId fn_id) {
// [0-ctz,1-clz][0-8,1-16,2-32,3-64]
size_t index0 = (fn_id == BuiltinFnIdCtz) ? 0 : 1;
size_t index1 = bits_index(int_type->data.integral.bit_count);
LLVMValueRef *fn = &g->int_builtin_fns[index0][index1];
if (!*fn) {
const char *fn_name = (fn_id == BuiltinFnIdCtz) ? "cttz" : "ctlz";
Buf *llvm_name = buf_sprintf("llvm.%s.i%zu", fn_name, int_type->data.integral.bit_count);
LLVMTypeRef param_types[] = {
int_type->type_ref,
LLVMInt1Type(),
};
LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, 2, false);
*fn = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);
}
return *fn;
}
static LLVMValueRef ir_render_clz(CodeGen *g, IrExecutable *executable, IrInstructionClz *instruction) {
TypeTableEntry *int_type = instruction->base.type_entry;
LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdClz);
LLVMValueRef operand = ir_llvm_value(g, instruction->value);
LLVMValueRef params[] {
operand,
LLVMConstNull(LLVMInt1Type()),
};
return LLVMBuildCall(g->builder, fn_val, params, 2, "");
}
static LLVMValueRef ir_render_ctz(CodeGen *g, IrExecutable *executable, IrInstructionCtz *instruction) {
TypeTableEntry *int_type = instruction->base.type_entry;
LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdCtz);
LLVMValueRef operand = ir_llvm_value(g, instruction->value);
LLVMValueRef params[] {
operand,
LLVMConstNull(LLVMInt1Type()),
};
return LLVMBuildCall(g->builder, fn_val, params, 2, "");
}
static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) { static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) {
set_debug_source_node(g, instruction->source_node); set_debug_source_node(g, instruction->source_node);
@ -1529,6 +1569,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_test_null(g, executable, (IrInstructionTestNull *)instruction); return ir_render_test_null(g, executable, (IrInstructionTestNull *)instruction);
case IrInstructionIdUnwrapMaybe: case IrInstructionIdUnwrapMaybe:
return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapMaybe *)instruction); return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapMaybe *)instruction);
case IrInstructionIdClz:
return ir_render_clz(g, executable, (IrInstructionClz *)instruction);
case IrInstructionIdCtz:
return ir_render_ctz(g, executable, (IrInstructionCtz *)instruction);
case IrInstructionIdSwitchBr: case IrInstructionIdSwitchBr:
case IrInstructionIdPhi: case IrInstructionIdPhi:
case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitList:
@ -2774,8 +2818,8 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn_with_arg_count(g, BuiltinFnIdCUndef, "cUndef", 1); create_builtin_fn_with_arg_count(g, BuiltinFnIdCUndef, "cUndef", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdCompileVar, "compileVar", 1); create_builtin_fn_with_arg_count(g, BuiltinFnIdCompileVar, "compileVar", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdConstEval, "constEval", 1); create_builtin_fn_with_arg_count(g, BuiltinFnIdConstEval, "constEval", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdCtz, "ctz", 2); create_builtin_fn_with_arg_count(g, BuiltinFnIdCtz, "ctz", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdClz, "clz", 2); create_builtin_fn_with_arg_count(g, BuiltinFnIdClz, "clz", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdImport, "import", 1); create_builtin_fn_with_arg_count(g, BuiltinFnIdImport, "import", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdCImport, "cImport", 1); create_builtin_fn_with_arg_count(g, BuiltinFnIdCImport, "cImport", 1);
create_builtin_fn_with_arg_count(g, BuiltinFnIdErrName, "errorName", 1); create_builtin_fn_with_arg_count(g, BuiltinFnIdErrName, "errorName", 1);

View File

@ -234,6 +234,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapMaybe *) {
return IrInstructionIdUnwrapMaybe; return IrInstructionIdUnwrapMaybe;
} }
static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) {
return IrInstructionIdClz;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) {
return IrInstructionIdCtz;
}
template<typename T> template<typename T>
static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) { static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) {
T *special_instruction = allocate<T>(1); T *special_instruction = allocate<T>(1);
@ -924,6 +932,36 @@ static IrInstruction *ir_build_unwrap_maybe_from(IrBuilder *irb, IrInstruction *
return new_instruction; return new_instruction;
} }
static IrInstruction *ir_build_clz(IrBuilder *irb, AstNode *source_node, IrInstruction *value) {
IrInstructionClz *instruction = ir_build_instruction<IrInstructionClz>(irb, source_node);
instruction->value = value;
ir_ref_instruction(value);
return &instruction->base;
}
static IrInstruction *ir_build_clz_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) {
IrInstruction *new_instruction = ir_build_clz(irb, old_instruction->source_node, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_ctz(IrBuilder *irb, AstNode *source_node, IrInstruction *value) {
IrInstructionCtz *instruction = ir_build_instruction<IrInstructionCtz>(irb, source_node);
instruction->value = value;
ir_ref_instruction(value);
return &instruction->base;
}
static IrInstruction *ir_build_ctz_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) {
IrInstruction *new_instruction = ir_build_ctz(irb, old_instruction->source_node, value);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block, static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block,
bool gen_error_defers, bool gen_maybe_defers) bool gen_error_defers, bool gen_maybe_defers)
{ {
@ -964,9 +1002,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, AstNode *node) {
return ir_build_return(irb, node, return_value); return ir_build_return(irb, node, return_value);
} }
case ReturnKindError: case ReturnKindError:
zig_panic("TODO %%return"); zig_panic("TODO gen IR for %%return");
case ReturnKindMaybe: case ReturnKindMaybe:
zig_panic("TODO ?return"); zig_panic("TODO gen IR for ?return");
} }
zig_unreachable(); zig_unreachable();
} }
@ -1188,7 +1226,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, AstNode *node) {
case BinOpTypeArrayMult: case BinOpTypeArrayMult:
return ir_gen_bin_op_id(irb, node, IrBinOpArrayMult); return ir_gen_bin_op_id(irb, node, IrBinOpArrayMult);
case BinOpTypeUnwrapMaybe: case BinOpTypeUnwrapMaybe:
zig_panic("TODO gen IR for unwrap maybe"); zig_panic("TODO gen IR for unwrap maybe binary operation");
} }
zig_unreachable(); zig_unreachable();
} }
@ -1357,7 +1395,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
if (builtin_fn->param_count != actual_param_count) { if (builtin_fn->param_count != actual_param_count) {
add_node_error(irb->codegen, node, add_node_error(irb->codegen, node,
buf_sprintf("expected %zu arguments, got %zu", buf_sprintf("expected %zu arguments, found %zu",
builtin_fn->param_count, actual_param_count)); builtin_fn->param_count, actual_param_count));
return irb->codegen->invalid_instruction; return irb->codegen->invalid_instruction;
} }
@ -1423,6 +1461,24 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
return ir_build_size_of(irb, node, arg0_value); return ir_build_size_of(irb, node, arg0_value);
} }
case BuiltinFnIdCtz:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, node->block_context);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_ctz(irb, node, arg0_value);
}
case BuiltinFnIdClz:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, node->block_context);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;
return ir_build_clz(irb, node, arg0_value);
}
case BuiltinFnIdMemcpy: case BuiltinFnIdMemcpy:
case BuiltinFnIdMemset: case BuiltinFnIdMemset:
case BuiltinFnIdAlignof: case BuiltinFnIdAlignof:
@ -1438,8 +1494,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
case BuiltinFnIdCUndef: case BuiltinFnIdCUndef:
case BuiltinFnIdCompileErr: case BuiltinFnIdCompileErr:
case BuiltinFnIdConstEval: case BuiltinFnIdConstEval:
case BuiltinFnIdCtz:
case BuiltinFnIdClz:
case BuiltinFnIdImport: case BuiltinFnIdImport:
case BuiltinFnIdCImport: case BuiltinFnIdCImport:
case BuiltinFnIdErrName: case BuiltinFnIdErrName:
@ -1547,13 +1601,17 @@ static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, AstNode *node, IrUnOp
return ir_gen_prefix_op_id_lval(irb, node, op_id, LValPurposeNone); return ir_gen_prefix_op_id_lval(irb, node, op_id, LValPurposeNone);
} }
static IrInstruction *ir_gen_prefix_op_unwrap_maybe(IrBuilder *irb, AstNode *node) { static IrInstruction *ir_gen_prefix_op_unwrap_maybe(IrBuilder *irb, AstNode *node, LValPurpose lval) {
AstNode *expr = node->data.prefix_op_expr.primary_expr; AstNode *expr = node->data.prefix_op_expr.primary_expr;
IrInstruction *value = ir_gen_node(irb, expr, node->block_context); IrInstruction *value = ir_gen_node_extra(irb, expr, node->block_context, LValPurposeAddressOf);
if (value == irb->codegen->invalid_instruction) if (value == irb->codegen->invalid_instruction)
return value; return value;
return ir_build_unwrap_maybe(irb, node, value, true); IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, node, value, true);
if (lval == LValPurposeNone)
return ir_build_load_ptr(irb, node, unwrapped_ptr);
else
return unwrapped_ptr;
} }
static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, AstNode *node, LValPurpose lval) { static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, AstNode *node, LValPurpose lval) {
@ -1585,7 +1643,7 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, AstNode *node, LValP
case PrefixOpUnwrapError: case PrefixOpUnwrapError:
return ir_gen_prefix_op_id(irb, node, IrUnOpUnwrapError); return ir_gen_prefix_op_id(irb, node, IrUnOpUnwrapError);
case PrefixOpUnwrapMaybe: case PrefixOpUnwrapMaybe:
return ir_gen_prefix_op_unwrap_maybe(irb, node); return ir_gen_prefix_op_unwrap_maybe(irb, node, lval);
} }
zig_unreachable(); zig_unreachable();
} }
@ -2349,7 +2407,7 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst
return result; return result;
} else { } else {
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->source_node, IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->source_node,
dest_type->other, value->other, cast_op); dest_type, value, cast_op);
result->type_entry = wanted_type; result->type_entry = wanted_type;
if (need_alloca && source_instr->source_node->block_context->fn_entry) { if (need_alloca && source_instr->source_node->block_context->fn_entry) {
IrInstructionCast *cast_instruction = (IrInstructionCast *)result; IrInstructionCast *cast_instruction = (IrInstructionCast *)result;
@ -2776,7 +2834,7 @@ static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value,
switch (result) { switch (result) {
case ImplicitCastMatchResultNo: case ImplicitCastMatchResultNo:
add_node_error(ira->codegen, first_executing_node(value->source_node), add_node_error(ira->codegen, first_executing_node(value->source_node),
buf_sprintf("expected type '%s', got '%s'", buf_sprintf("expected type '%s', found '%s'",
buf_ptr(&expected_type->name), buf_ptr(&expected_type->name),
buf_ptr(&value->type_entry->name))); buf_ptr(&value->type_entry->name)));
return ira->codegen->invalid_instruction; return ira->codegen->invalid_instruction;
@ -3346,7 +3404,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
return ira->codegen->builtin_types.entry_invalid; return ira->codegen->builtin_types.entry_invalid;
} }
IrInstruction *arg = call_instruction->args[0]; IrInstruction *arg = call_instruction->args[0]->other;
IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, fn_ref, arg); IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, fn_ref, arg);
if (cast_instruction == ira->codegen->invalid_instruction) if (cast_instruction == ira->codegen->invalid_instruction)
return ira->codegen->builtin_types.entry_invalid; return ira->codegen->builtin_types.entry_invalid;
@ -3701,7 +3759,7 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
// return type_entry->data.error.child_type; // return type_entry->data.error.child_type;
// } else { // } else {
// add_node_error(g, *expr_node, // add_node_error(g, *expr_node,
// buf_sprintf("expected error type, got '%s'", buf_ptr(&type_entry->name))); // buf_sprintf("expected error type, found '%s'", buf_ptr(&type_entry->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
//} //}
@ -4175,6 +4233,7 @@ static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstru
if (ptr->static_value.special != ConstValSpecialRuntime) { if (ptr->static_value.special != ConstValSpecialRuntime) {
// This memory location is transforming from known at compile time to known at runtime. // This memory location is transforming from known at compile time to known at runtime.
// We must emit our own var ptr instruction. // We must emit our own var ptr instruction.
// TODO can we delete this code now that we have inline var?
ptr->static_value.special = ConstValSpecialRuntime; ptr->static_value.special = ConstValSpecialRuntime;
IrInstruction *new_ptr_inst; IrInstruction *new_ptr_inst;
if (ptr->id == IrInstructionIdVarPtr) { if (ptr->id == IrInstructionIdVarPtr) {
@ -4347,12 +4406,12 @@ static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,
target_context = type_arg->data.unionation.block_context; target_context = type_arg->data.unionation.block_context;
} else { } else {
add_node_error(ira->codegen, target_instruction->source_node, add_node_error(ira->codegen, target_instruction->source_node,
buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&type_arg->name))); buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&type_arg->name)));
return ira->codegen->builtin_types.entry_invalid; return ira->codegen->builtin_types.entry_invalid;
} }
} else { } else {
add_node_error(ira->codegen, target_instruction->source_node, add_node_error(ira->codegen, target_instruction->source_node,
buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&target_type->name))); buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&target_type->name)));
return ira->codegen->builtin_types.entry_invalid; return ira->codegen->builtin_types.entry_invalid;
} }
@ -4682,6 +4741,54 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
return result_type; return result_type;
} }
static TypeTableEntry *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) {
IrInstruction *value = ctz_instruction->value->other;
if (value->type_entry->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (value->type_entry->id == TypeTableEntryIdInt) {
if (value->static_value.special != ConstValSpecialRuntime) {
uint32_t result = bignum_ctz(&value->static_value.data.x_bignum,
value->type_entry->data.integral.bit_count);
bool depends_on_compile_var = value->static_value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &ctz_instruction->base,
depends_on_compile_var);
bignum_init_unsigned(&out_val->data.x_bignum, result);
return value->type_entry;
}
ir_build_ctz_from(&ira->new_irb, &ctz_instruction->base, value);
return value->type_entry;
} else {
add_node_error(ira->codegen, ctz_instruction->base.source_node,
buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->type_entry->name)));
return ira->codegen->builtin_types.entry_invalid;
}
}
static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionClz *clz_instruction) {
IrInstruction *value = clz_instruction->value->other;
if (value->type_entry->id == TypeTableEntryIdInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (value->type_entry->id == TypeTableEntryIdInt) {
if (value->static_value.special != ConstValSpecialRuntime) {
uint32_t result = bignum_clz(&value->static_value.data.x_bignum,
value->type_entry->data.integral.bit_count);
bool depends_on_compile_var = value->static_value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &clz_instruction->base,
depends_on_compile_var);
bignum_init_unsigned(&out_val->data.x_bignum, result);
return value->type_entry;
}
ir_build_clz_from(&ira->new_irb, &clz_instruction->base, value);
return value->type_entry;
} else {
add_node_error(ira->codegen, clz_instruction->base.source_node,
buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->type_entry->name)));
return ira->codegen->builtin_types.entry_invalid;
}
}
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) { switch (instruction->id) {
case IrInstructionIdInvalid: case IrInstructionIdInvalid:
@ -4742,6 +4849,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_test_null(ira, (IrInstructionTestNull *)instruction); return ir_analyze_instruction_test_null(ira, (IrInstructionTestNull *)instruction);
case IrInstructionIdUnwrapMaybe: case IrInstructionIdUnwrapMaybe:
return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapMaybe *)instruction); return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapMaybe *)instruction);
case IrInstructionIdClz:
return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction);
case IrInstructionIdCtz:
return ir_analyze_instruction_ctz(ira, (IrInstructionCtz *)instruction);
case IrInstructionIdSwitchBr: case IrInstructionIdSwitchBr:
case IrInstructionIdCast: case IrInstructionIdCast:
case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitList:
@ -4854,6 +4965,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdSizeOf: case IrInstructionIdSizeOf:
case IrInstructionIdTestNull: case IrInstructionIdTestNull:
case IrInstructionIdUnwrapMaybe: case IrInstructionIdUnwrapMaybe:
case IrInstructionIdClz:
case IrInstructionIdCtz:
return false; return false;
case IrInstructionIdAsm: case IrInstructionIdAsm:
{ {
@ -5117,7 +5230,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } else if (ptr_type->id != TypeTableEntryIdPointer) { // } else if (ptr_type->id != TypeTableEntryIdPointer) {
// add_node_error(g, *ptr_arg, // add_node_error(g, *ptr_arg,
// buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&ptr_type->name))); // buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&ptr_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
// //
@ -5223,7 +5336,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// zig_panic("TODO"); // zig_panic("TODO");
// } else { // } else {
// add_node_error(g, node, // add_node_error(g, node,
// buf_sprintf("expected integer type, got '%s'", buf_ptr(&result_type->name))); // buf_sprintf("expected integer type, found '%s'", buf_ptr(&result_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
//} //}
@ -5243,16 +5356,16 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } else if (dest_type->id != TypeTableEntryIdInt) { // } else if (dest_type->id != TypeTableEntryIdInt) {
// add_node_error(g, *op1, // add_node_error(g, *op1,
// buf_sprintf("expected integer type, got '%s'", buf_ptr(&dest_type->name))); // buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } else if (src_type->id != TypeTableEntryIdInt) { // } else if (src_type->id != TypeTableEntryIdInt) {
// add_node_error(g, *op2, // add_node_error(g, *op2,
// buf_sprintf("expected integer type, got '%s'", buf_ptr(&src_type->name))); // buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } else if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) { // } else if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) {
// const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned"; // const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned";
// add_node_error(g, *op2, // add_node_error(g, *op2,
// buf_sprintf("expected %s integer type, got '%s'", sign_str, buf_ptr(&src_type->name))); // buf_sprintf("expected %s integer type, found '%s'", sign_str, buf_ptr(&src_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } else if (src_type->data.integral.bit_count <= dest_type->data.integral.bit_count) { // } else if (src_type->data.integral.bit_count <= dest_type->data.integral.bit_count) {
// add_node_error(g, *op2, // add_node_error(g, *op2,
@ -5459,7 +5572,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// result_node); // result_node);
// } else { // } else {
// add_node_error(g, type_node, // add_node_error(g, type_node,
// buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name))); // buf_sprintf("expected integer type, found '%s'", buf_ptr(&int_type->name)));
// } // }
// //
// // TODO constant expression evaluation // // TODO constant expression evaluation
@ -5479,14 +5592,14 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// dest_type->id != TypeTableEntryIdPointer) // dest_type->id != TypeTableEntryIdPointer)
// { // {
// add_node_error(g, dest_node, // add_node_error(g, dest_node,
// buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name))); // buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&dest_type->name)));
// } // }
// //
// if (src_type->id != TypeTableEntryIdInvalid && // if (src_type->id != TypeTableEntryIdInvalid &&
// src_type->id != TypeTableEntryIdPointer) // src_type->id != TypeTableEntryIdPointer)
// { // {
// add_node_error(g, src_node, // add_node_error(g, src_node,
// buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&src_type->name))); // buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&src_type->name)));
// } // }
// //
// if (dest_type->id == TypeTableEntryIdPointer && // if (dest_type->id == TypeTableEntryIdPointer &&
@ -5517,7 +5630,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// dest_type->id != TypeTableEntryIdPointer) // dest_type->id != TypeTableEntryIdPointer)
// { // {
// add_node_error(g, dest_node, // add_node_error(g, dest_node,
// buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name))); // buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&dest_type->name)));
// } // }
// //
// return builtin_fn->return_type; // return builtin_fn->return_type;
@ -5622,29 +5735,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// //
// return resolved_type; // return resolved_type;
// } // }
// case BuiltinFnIdCtz:
// case BuiltinFnIdClz:
// {
// AstNode *type_node = node->data.fn_call_expr.params.at(0);
// TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node);
// if (int_type->id == TypeTableEntryIdInvalid) {
// return int_type;
// } else if (int_type->id == TypeTableEntryIdInt) {
// AstNode **expr_node = node->data.fn_call_expr.params.at(1)->parent_field;
// TypeTableEntry *resolved_type = analyze_expression(g, import, context, int_type, *expr_node);
// if (resolved_type->id == TypeTableEntryIdInvalid) {
// return resolved_type;
// }
//
// // TODO const expr eval
//
// return resolved_type;
// } else {
// add_node_error(g, type_node,
// buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name)));
// return g->builtin_types.entry_invalid;
// }
// }
// case BuiltinFnIdImport: // case BuiltinFnIdImport:
// return analyze_import(g, import, context, node); // return analyze_import(g, import, context, node);
// case BuiltinFnIdCImport: // case BuiltinFnIdCImport:
@ -6192,7 +6282,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// return child_type; // return child_type;
// } else { // } else {
// add_node_error(g, op1, // add_node_error(g, op1,
// buf_sprintf("expected maybe type, got '%s'", // buf_sprintf("expected maybe type, found '%s'",
// buf_ptr(&lhs_type->name))); // buf_ptr(&lhs_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
@ -6212,7 +6302,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// op1_type->data.pointer.child_type == g->builtin_types.entry_u8) { // op1_type->data.pointer.child_type == g->builtin_types.entry_u8) {
// child_type = op1_type->data.pointer.child_type; // child_type = op1_type->data.pointer.child_type;
// } else { // } else {
// add_node_error(g, *op1, buf_sprintf("expected array or C string literal, got '%s'", // add_node_error(g, *op1, buf_sprintf("expected array or C string literal, found '%s'",
// buf_ptr(&op1_type->name))); // buf_ptr(&op1_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
@ -6223,7 +6313,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } else if (op2_type->id == TypeTableEntryIdArray) { // } else if (op2_type->id == TypeTableEntryIdArray) {
// if (op2_type->data.array.child_type != child_type) { // if (op2_type->data.array.child_type != child_type) {
// add_node_error(g, *op2, buf_sprintf("expected array of type '%s', got '%s'", // add_node_error(g, *op2, buf_sprintf("expected array of type '%s', found '%s'",
// buf_ptr(&child_type->name), // buf_ptr(&child_type->name),
// buf_ptr(&op2_type->name))); // buf_ptr(&op2_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
@ -6231,7 +6321,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// } else if (op2_type->id == TypeTableEntryIdPointer && // } else if (op2_type->id == TypeTableEntryIdPointer &&
// op2_type->data.pointer.child_type == g->builtin_types.entry_u8) { // op2_type->data.pointer.child_type == g->builtin_types.entry_u8) {
// } else { // } else {
// add_node_error(g, *op2, buf_sprintf("expected array or C string literal, got '%s'", // add_node_error(g, *op2, buf_sprintf("expected array or C string literal, found '%s'",
// buf_ptr(&op2_type->name))); // buf_ptr(&op2_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
@ -6271,12 +6361,12 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// } else if (op1_type->id == TypeTableEntryIdPointer) { // } else if (op1_type->id == TypeTableEntryIdPointer) {
// if (!op1_val->data.x_ptr.is_c_str) { // if (!op1_val->data.x_ptr.is_c_str) {
// add_node_error(g, *op1, // add_node_error(g, *op1,
// buf_sprintf("expected array or C string literal, got '%s'", // buf_sprintf("expected array or C string literal, found '%s'",
// buf_ptr(&op1_type->name))); // buf_ptr(&op1_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } else if (!op2_val->data.x_ptr.is_c_str) { // } else if (!op2_val->data.x_ptr.is_c_str) {
// add_node_error(g, *op2, // add_node_error(g, *op2,
// buf_sprintf("expected array or C string literal, got '%s'", // buf_sprintf("expected array or C string literal, found '%s'",
// buf_ptr(&op2_type->name))); // buf_ptr(&op2_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
@ -6510,12 +6600,12 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// if (call_param_count < expect_arg_count) { // if (call_param_count < expect_arg_count) {
// ok_invocation = false; // ok_invocation = false;
// add_node_error(g, node, // add_node_error(g, node,
// buf_sprintf("expected at least %zu arguments, got %zu", src_param_count, call_param_count)); // buf_sprintf("expected at least %zu arguments, found %zu", src_param_count, call_param_count));
// } // }
// } else if (expect_arg_count != call_param_count) { // } else if (expect_arg_count != call_param_count) {
// ok_invocation = false; // ok_invocation = false;
// add_node_error(g, node, // add_node_error(g, node,
// buf_sprintf("expected %zu arguments, got %zu", expect_arg_count, call_param_count)); // buf_sprintf("expected %zu arguments, found %zu", expect_arg_count, call_param_count));
// } // }
// //
// bool all_args_const_expr = true; // bool all_args_const_expr = true;
@ -6631,7 +6721,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// //
// if (src_param_count != call_param_count + struct_node_1_or_0) { // if (src_param_count != call_param_count + struct_node_1_or_0) {
// add_node_error(g, call_node, // add_node_error(g, call_node,
// buf_sprintf("expected %zu arguments, got %zu", src_param_count - struct_node_1_or_0, call_param_count)); // buf_sprintf("expected %zu arguments, found %zu", src_param_count - struct_node_1_or_0, call_param_count));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
// //
@ -6759,7 +6849,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// //
// if (actual_param_count != expected_param_count) { // if (actual_param_count != expected_param_count) {
// add_node_error(g, first_executing_node(node), // add_node_error(g, first_executing_node(node),
// buf_sprintf("expected %zu arguments, got %zu", expected_param_count, actual_param_count)); // buf_sprintf("expected %zu arguments, found %zu", expected_param_count, actual_param_count));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
// //
@ -7180,7 +7270,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// return resolved_type->data.error.child_type; // return resolved_type->data.error.child_type;
// } else { // } else {
// add_node_error(g, node->data.return_expr.expr, // add_node_error(g, node->data.return_expr.expr,
// buf_sprintf("expected error type, got '%s'", buf_ptr(&resolved_type->name))); // buf_sprintf("expected error type, found '%s'", buf_ptr(&resolved_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
// } // }
@ -7208,7 +7298,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// return resolved_type->data.maybe.child_type; // return resolved_type->data.maybe.child_type;
// } else { // } else {
// add_node_error(g, node->data.return_expr.expr, // add_node_error(g, node->data.return_expr.expr,
// buf_sprintf("expected maybe type, got '%s'", buf_ptr(&resolved_type->name))); // buf_sprintf("expected maybe type, found '%s'", buf_ptr(&resolved_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
// } // }
@ -7502,14 +7592,14 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// //
// if (op1_type->id != TypeTableEntryIdArray) { // if (op1_type->id != TypeTableEntryIdArray) {
// add_node_error(g, *op1, // add_node_error(g, *op1,
// buf_sprintf("expected array type, got '%s'", buf_ptr(&op1_type->name))); // buf_sprintf("expected array type, found '%s'", buf_ptr(&op1_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
// //
// if (op2_type->id != TypeTableEntryIdNumLitInt && // if (op2_type->id != TypeTableEntryIdNumLitInt &&
// op2_type->id != TypeTableEntryIdInt) // op2_type->id != TypeTableEntryIdInt)
// { // {
// add_node_error(g, *op2, buf_sprintf("expected integer type, got '%s'", buf_ptr(&op2_type->name))); // add_node_error(g, *op2, buf_sprintf("expected integer type, found '%s'", buf_ptr(&op2_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
// //
@ -7576,7 +7666,7 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
// return child_type; // return child_type;
// } else { // } else {
// add_node_error(g, op1, // add_node_error(g, op1,
// buf_sprintf("expected error type, got '%s'", buf_ptr(&lhs_type->name))); // buf_sprintf("expected error type, found '%s'", buf_ptr(&lhs_type->name)));
// return g->builtin_types.entry_invalid; // return g->builtin_types.entry_invalid;
// } // }
//} //}
@ -8076,21 +8166,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
// case BuiltinFnIdCompileErr: // case BuiltinFnIdCompileErr:
// case BuiltinFnIdIntType: // case BuiltinFnIdIntType:
// zig_unreachable(); // zig_unreachable();
// case BuiltinFnIdCtz:
// case BuiltinFnIdClz:
// {
// size_t fn_call_param_count = node->data.fn_call_expr.params.length;
// assert(fn_call_param_count == 2);
// TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
// assert(int_type->id == TypeTableEntryIdInt);
// LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, builtin_fn->id);
// LLVMValueRef operand = gen_expr(g, node->data.fn_call_expr.params.at(1));
// LLVMValueRef params[] {
// operand,
// LLVMConstNull(LLVMInt1Type()),
// };
// return LLVMBuildCall(g->builder, fn_val, params, 2, "");
// }
// case BuiltinFnIdAddWithOverflow: // case BuiltinFnIdAddWithOverflow:
// case BuiltinFnIdSubWithOverflow: // case BuiltinFnIdSubWithOverflow:
// case BuiltinFnIdMulWithOverflow: // case BuiltinFnIdMulWithOverflow:
@ -9539,24 +9614,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
// return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val, &init_val_type, false); // return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val, &init_val_type, false);
//} //}
// //
//static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, BuiltinFnId fn_id) {
// // [0-ctz,1-clz][0-8,1-16,2-32,3-64]
// size_t index0 = (fn_id == BuiltinFnIdCtz) ? 0 : 1;
// size_t index1 = bits_index(int_type->data.integral.bit_count);
// LLVMValueRef *fn = &g->int_builtin_fns[index0][index1];
// if (!*fn) {
// const char *fn_name = (fn_id == BuiltinFnIdCtz) ? "cttz" : "ctlz";
// Buf *llvm_name = buf_sprintf("llvm.%s.i%zu", fn_name, int_type->data.integral.bit_count);
// LLVMTypeRef param_types[] = {
// int_type->type_ref,
// LLVMInt1Type(),
// };
// LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, 2, false);
// *fn = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);
// }
// return *fn;
//}
//
//static LLVMValueRef gen_fence(CodeGen *g, AstNode *node) { //static LLVMValueRef gen_fence(CodeGen *g, AstNode *node) {
// assert(node->type == NodeTypeFnCallExpr); // assert(node->type == NodeTypeFnCallExpr);
// //

View File

@ -511,6 +511,18 @@ static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instru
} }
} }
static void ir_print_clz(IrPrint *irp, IrInstructionClz *instruction) {
fprintf(irp->f, "@clz(");
ir_print_other_instruction(irp, instruction->value);
fprintf(irp->f, ")");
}
static void ir_print_ctz(IrPrint *irp, IrInstructionCtz *instruction) {
fprintf(irp->f, "@ctz(");
ir_print_other_instruction(irp, instruction->value);
fprintf(irp->f, ")");
}
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction); ir_print_prefix(irp, instruction);
switch (instruction->id) { switch (instruction->id) {
@ -612,6 +624,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdUnwrapMaybe: case IrInstructionIdUnwrapMaybe:
ir_print_unwrap_maybe(irp, (IrInstructionUnwrapMaybe *)instruction); ir_print_unwrap_maybe(irp, (IrInstructionUnwrapMaybe *)instruction);
break; break;
case IrInstructionIdCtz:
ir_print_ctz(irp, (IrInstructionCtz *)instruction);
break;
case IrInstructionIdClz:
ir_print_clz(irp, (IrInstructionClz *)instruction);
break;
case IrInstructionIdSwitchBr: case IrInstructionIdSwitchBr:
zig_panic("TODO print more IR instructions"); zig_panic("TODO print more IR instructions");
} }