IR: add minValue, maxValue, and negation

This commit is contained in:
Andrew Kelley 2016-12-07 01:23:38 -05:00
parent 5e4ee659a6
commit 0ad580f001
6 changed files with 239 additions and 82 deletions

View File

@ -1393,6 +1393,8 @@ enum IrInstructionId {
IrInstructionIdImport,
IrInstructionIdArrayLen,
IrInstructionIdRef,
IrInstructionIdMinValue,
IrInstructionIdMaxValue,
};
struct IrInstruction {
@ -1791,6 +1793,18 @@ struct IrInstructionRef {
LLVMValueRef tmp_ptr;
};
struct IrInstructionMinValue {
IrInstruction base;
IrInstruction *value;
};
struct IrInstructionMaxValue {
IrInstruction base;
IrInstruction *value;
};
enum LValPurpose {
LValPurposeNone,
LValPurposeAssign,

View File

@ -1821,6 +1821,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdStaticEval:
case IrInstructionIdImport:
case IrInstructionIdContainerInitFields:
case IrInstructionIdMinValue:
case IrInstructionIdMaxValue:
zig_unreachable();
case IrInstructionIdReturn:
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);

View File

@ -431,6 +431,8 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *
} else if (type_entry->id == TypeTableEntryIdBool) {
const_val->special = ConstValSpecialStatic;
const_val->data.x_bool = is_max;
} else if (type_entry->id == TypeTableEntryIdVoid) {
// nothing to do
} else {
zig_unreachable();
}

View File

@ -306,6 +306,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) {
return IrInstructionIdStructInit;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMinValue *) {
return IrInstructionIdMinValue;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMaxValue *) {
return IrInstructionIdMaxValue;
}
template<typename T>
static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate<T>(1);
@ -1241,6 +1249,25 @@ static IrInstruction *ir_build_ref_from(IrBuilder *irb, IrInstruction *old_instr
return new_instruction;
}
static IrInstruction *ir_build_min_value(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionMinValue *instruction = ir_build_instruction<IrInstructionMinValue>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value);
return &instruction->base;
}
static IrInstruction *ir_build_max_value(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
IrInstructionMaxValue *instruction = ir_build_instruction<IrInstructionMaxValue>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value);
return &instruction->base;
}
static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope,
bool gen_error_defers, bool gen_maybe_defers)
{
@ -1879,11 +1906,27 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_build_import(irb, scope, node, arg0_value);
}
case BuiltinFnIdMaxValue:
{
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;
return ir_build_max_value(irb, scope, node, arg0_value);
}
case BuiltinFnIdMinValue:
{
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;
return ir_build_min_value(irb, scope, node, arg0_value);
}
case BuiltinFnIdMemcpy:
case BuiltinFnIdMemset:
case BuiltinFnIdAlignof:
case BuiltinFnIdMaxValue:
case BuiltinFnIdMinValue:
case BuiltinFnIdMemberCount:
case BuiltinFnIdAddWithOverflow:
case BuiltinFnIdSubWithOverflow:
@ -4670,7 +4713,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
if (is_inline || fn_ref->static_value.special != ConstValSpecialRuntime) {
if (fn_ref->type_entry->id == TypeTableEntryIdMetaType) {
TypeTableEntry *dest_type = ir_resolve_type(ira, fn_ref);
if (!dest_type)
if (dest_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
size_t actual_param_count = call_instruction->arg_count;
@ -4958,6 +5001,48 @@ static TypeTableEntry *ir_analyze_unwrap_maybe(IrAnalyze *ira, IrInstructionUnOp
}
}
static TypeTableEntry *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
IrInstruction *value = un_op_instruction->value->other;
TypeTableEntry *expr_type = value->type_entry;
if (expr_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
bool is_wrap_op = (un_op_instruction->op_id == IrUnOpNegationWrap);
if ((expr_type->id == TypeTableEntryIdInt && expr_type->data.integral.is_signed) ||
expr_type->id == TypeTableEntryIdNumLitInt ||
((expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitFloat) &&
!is_wrap_op))
{
ConstExprValue *target_const_val = &value->static_value;
if (target_const_val->special != ConstValSpecialRuntime) {
bool depends_on_compile_var = value->static_value.depends_on_compile_var;
ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base, depends_on_compile_var);
bignum_negate(&out_val->data.x_bignum, &target_const_val->data.x_bignum);
if (expr_type->id == TypeTableEntryIdFloat ||
expr_type->id == TypeTableEntryIdNumLitFloat ||
expr_type->id == TypeTableEntryIdNumLitInt)
{
return expr_type;
}
bool overflow = !bignum_fits_in_bits(&out_val->data.x_bignum, expr_type->data.integral.bit_count, true);
if (is_wrap_op) {
if (overflow)
out_val->data.x_bignum.is_negative = true;
} else if (overflow) {
ir_add_error(ira, &un_op_instruction->base, buf_sprintf("negation caused overflow"));
return ira->codegen->builtin_types.entry_invalid;
}
return expr_type;
}
}
const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";
ir_add_error(ira, &un_op_instruction->base, buf_sprintf(fmt, buf_ptr(&expr_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
IrUnOp op_id = un_op_instruction->op_id;
switch (op_id) {
@ -4983,51 +5068,7 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
//}
case IrUnOpNegation:
case IrUnOpNegationWrap:
zig_panic("TODO analyze PrefixOpNegation[Wrap]");
//{
// TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node);
// if (expr_type->id == TypeTableEntryIdInvalid) {
// return expr_type;
// } else if ((expr_type->id == TypeTableEntryIdInt &&
// expr_type->data.integral.is_signed) ||
// expr_type->id == TypeTableEntryIdNumLitInt ||
// ((expr_type->id == TypeTableEntryIdFloat ||
// expr_type->id == TypeTableEntryIdNumLitFloat) &&
// prefix_op != PrefixOpNegationWrap))
// {
// ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val;
// if (!target_const_val->ok) {
// return expr_type;
// }
// ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
// const_val->ok = true;
// const_val->depends_on_compile_var = target_const_val->depends_on_compile_var;
// bignum_negate(&const_val->data.x_bignum, &target_const_val->data.x_bignum);
// if (expr_type->id == TypeTableEntryIdFloat ||
// expr_type->id == TypeTableEntryIdNumLitFloat ||
// expr_type->id == TypeTableEntryIdNumLitInt)
// {
// return expr_type;
// }
// bool overflow = !bignum_fits_in_bits(&const_val->data.x_bignum,
// expr_type->data.integral.bit_count, expr_type->data.integral.is_signed);
// if (prefix_op == PrefixOpNegationWrap) {
// if (overflow) {
// const_val->data.x_bignum.is_negative = true;
// }
// } else if (overflow) {
// add_node_error(g, *expr_node, buf_sprintf("negation caused overflow"));
// return g->builtin_types.entry_invalid;
// }
// return expr_type;
// } else {
// const char *fmt = (prefix_op == PrefixOpNegationWrap) ?
// "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";
// add_node_error(g, node, buf_sprintf(fmt, buf_ptr(&expr_type->name)));
// return g->builtin_types.entry_invalid;
// }
//}
return ir_analyze_negation(ira, un_op_instruction);
case IrUnOpAddressOf:
case IrUnOpConstAddressOf:
return ir_analyze_unary_address_of(ira, un_op_instruction, op_id == IrUnOpConstAddressOf);
@ -6575,7 +6616,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru
static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira, IrInstructionContainerInitList *instruction) {
IrInstruction *container_type_value = instruction->container_type->other;
TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
if (!container_type)
if (container_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
size_t elem_count = instruction->item_count;
@ -6661,7 +6702,7 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira
static TypeTableEntry *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) {
IrInstruction *container_type_value = instruction->container_type->other;
TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
if (!container_type)
if (container_type->id == TypeTableEntryIdInvalid)
return ira->codegen->builtin_types.entry_invalid;
bool depends_on_compile_var = container_type_value->static_value.depends_on_compile_var;
@ -6670,6 +6711,77 @@ static TypeTableEntry *ir_analyze_instruction_container_init_fields(IrAnalyze *i
instruction->field_count, instruction->fields, depends_on_compile_var);
}
static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_instruction,
IrInstruction *target_type_value, bool is_max)
{
TypeTableEntry *target_type = ir_resolve_type(ira, target_type_value);
bool depends_on_compile_var = target_type_value->static_value.depends_on_compile_var;
switch (target_type->id) {
case TypeTableEntryIdInvalid:
return ira->codegen->builtin_types.entry_invalid;
case TypeTableEntryIdInt:
{
ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var);
eval_min_max_value(ira->codegen, target_type, out_val, is_max);
return ira->codegen->builtin_types.entry_num_lit_int;
}
case TypeTableEntryIdFloat:
{
ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var);
eval_min_max_value(ira->codegen, target_type, out_val, is_max);
return ira->codegen->builtin_types.entry_num_lit_float;
}
case TypeTableEntryIdBool:
case TypeTableEntryIdVoid:
{
ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var);
eval_min_max_value(ira->codegen, target_type, out_val, is_max);
return target_type;
}
case TypeTableEntryIdVar:
case TypeTableEntryIdMetaType:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
case TypeTableEntryIdNumLitFloat:
case TypeTableEntryIdNumLitInt:
case TypeTableEntryIdUndefLit:
case TypeTableEntryIdNullLit:
case TypeTableEntryIdMaybe:
case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdPureError:
case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion:
case TypeTableEntryIdFn:
case TypeTableEntryIdTypeDecl:
case TypeTableEntryIdNamespace:
case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn:
{
const char *err_format = is_max ?
"no max value available for type '%s'" :
"no min value available for type '%s'";
ir_add_error(ira, source_instruction,
buf_sprintf(err_format, buf_ptr(&target_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_min_value(IrAnalyze *ira,
IrInstructionMinValue *instruction)
{
return ir_analyze_min_max(ira, &instruction->base, instruction->value->other, false);
}
static TypeTableEntry *ir_analyze_instruction_max_value(IrAnalyze *ira,
IrInstructionMaxValue *instruction)
{
return ir_analyze_min_max(ira, &instruction->base, instruction->value->other, true);
}
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
@ -6754,6 +6866,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_container_init_list(ira, (IrInstructionContainerInitList *)instruction);
case IrInstructionIdContainerInitFields:
return ir_analyze_instruction_container_init_fields(ira, (IrInstructionContainerInitFields *)instruction);
case IrInstructionIdMinValue:
return ir_analyze_instruction_min_value(ira, (IrInstructionMinValue *)instruction);
case IrInstructionIdMaxValue:
return ir_analyze_instruction_max_value(ira, (IrInstructionMaxValue *)instruction);
case IrInstructionIdCast:
case IrInstructionIdStructFieldPtr:
case IrInstructionIdEnumFieldPtr:
@ -6880,6 +6996,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdEnumTag:
case IrInstructionIdStaticEval:
case IrInstructionIdRef:
case IrInstructionIdMinValue:
case IrInstructionIdMaxValue:
return false;
case IrInstructionIdAsm:
{
@ -6892,32 +7010,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
// TODO port over all this commented out code into new IR way of doing things
//static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
// AstNode *node, const char *err_format, bool is_max)
//{
// assert(node->type == NodeTypeFnCallExpr);
// assert(node->data.fn_call_expr.params.length == 1);
//
// AstNode *type_node = node->data.fn_call_expr.params.at(0);
// TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
//
// if (type_entry->id == TypeTableEntryIdInvalid) {
// return g->builtin_types.entry_invalid;
// } else if (type_entry->id == TypeTableEntryIdInt) {
// eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max);
// return g->builtin_types.entry_num_lit_int;
// } else if (type_entry->id == TypeTableEntryIdFloat) {
// eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max);
// return g->builtin_types.entry_num_lit_float;
// } else if (type_entry->id == TypeTableEntryIdBool) {
// eval_min_max_value(g, type_entry, &get_resolved_expr(node)->const_val, is_max);
// return type_entry;
// } else {
// add_node_error(g, node,
// buf_sprintf(err_format, buf_ptr(&type_entry->name)));
// return g->builtin_types.entry_invalid;
// }
//}
//static TypeTableEntry *analyze_c_import(CodeGen *g, ImportTableEntry *parent_import,
// BlockContext *parent_context, AstNode *node)
@ -7393,12 +7485,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
// align_in_bytes, false);
// }
// }
// case BuiltinFnIdMaxValue:
// return analyze_min_max_value(g, import, context, node,
// "no max value available for type '%s'", true);
// case BuiltinFnIdMinValue:
// return analyze_min_max_value(g, import, context, node,
// "no min value available for type '%s'", false);
// case BuiltinFnIdMemberCount:
// {
// AstNode *type_node = node->data.fn_call_expr.params.at(0);

View File

@ -658,6 +658,18 @@ static void ir_print_ref(IrPrint *irp, IrInstructionRef *instruction) {
ir_print_other_instruction(irp, instruction->value);
}
static void ir_print_min_value(IrPrint *irp, IrInstructionMinValue *instruction) {
fprintf(irp->f, "@minValue(");
ir_print_other_instruction(irp, instruction->value);
fprintf(irp->f, ")");
}
static void ir_print_max_value(IrPrint *irp, IrInstructionMaxValue *instruction) {
fprintf(irp->f, "@maxValue(");
ir_print_other_instruction(irp, instruction->value);
fprintf(irp->f, ")");
}
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
switch (instruction->id) {
@ -795,6 +807,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdRef:
ir_print_ref(irp, (IrInstructionRef *)instruction);
break;
case IrInstructionIdMinValue:
ir_print_min_value(irp, (IrInstructionMinValue *)instruction);
break;
case IrInstructionIdMaxValue:
ir_print_max_value(irp, (IrInstructionMaxValue *)instruction);
break;
}
fprintf(irp->f, "\n");
}

View File

@ -204,6 +204,39 @@ fn testStaticAddOne() {
assert(should_be_1235 == 1235);
}
fn gimme1or2(inline a: bool) -> i32 {
const x: i32 = 1;
const y: i32 = 2;
inline var z: i32 = inline if (a) x else y;
return z;
}
fn testInlineVarsAgain() {
assert(gimme1or2(true) == 1);
assert(gimme1or2(false) == 2);
}
fn testMinValueAndMaxValue() {
assert(@maxValue(u8) == 255);
assert(@maxValue(u16) == 65535);
assert(@maxValue(u32) == 4294967295);
assert(@maxValue(u64) == 18446744073709551615);
assert(@maxValue(i8) == 127);
assert(@maxValue(i16) == 32767);
assert(@maxValue(i32) == 2147483647);
assert(@maxValue(i64) == 9223372036854775807);
assert(@minValue(u8) == 0);
assert(@minValue(u16) == 0);
assert(@minValue(u32) == 0);
assert(@minValue(u64) == 0);
assert(@minValue(i8) == -128);
assert(@minValue(i16) == -32768);
assert(@minValue(i32) == -2147483648);
assert(@minValue(i64) == -9223372036854775808);
}
fn assert(ok: bool) {
if (!ok)
@ -228,6 +261,8 @@ fn runAllTests() {
shortCircuit();
testGotoLeaveDeferScope(true);
testStaticAddOne();
testInlineVarsAgain();
testMinValueAndMaxValue();
}
export nakedcc fn _start() -> unreachable {