IR: pass enumToInt test

master
Andrew Kelley 2016-12-26 02:36:04 -05:00
parent 1b5d1877ae
commit 4664f793dc
6 changed files with 124 additions and 47 deletions

View File

@ -429,14 +429,12 @@ enum CastOp {
CastOpNoop, // fn call expr is a cast, but does nothing CastOpNoop, // fn call expr is a cast, but does nothing
CastOpPtrToInt, CastOpPtrToInt,
CastOpIntToPtr, CastOpIntToPtr,
CastOpWidenOrShorten,
CastOpErrToInt, CastOpErrToInt,
CastOpIntToFloat, CastOpIntToFloat,
CastOpFloatToInt, CastOpFloatToInt,
CastOpBoolToInt, CastOpBoolToInt,
CastOpResizeSlice, CastOpResizeSlice,
CastOpIntToEnum, CastOpIntToEnum,
CastOpEnumToInt,
CastOpBytesToSlice, CastOpBytesToSlice,
}; };
@ -1454,6 +1452,7 @@ enum IrInstructionId {
IrInstructionIdTestComptime, IrInstructionIdTestComptime,
IrInstructionIdInitEnum, IrInstructionIdInitEnum,
IrInstructionIdPointerReinterpret, IrInstructionIdPointerReinterpret,
IrInstructionIdWidenOrShorten,
}; };
struct IrInstruction { struct IrInstruction {
@ -2096,6 +2095,12 @@ struct IrInstructionPointerReinterpret {
IrInstruction *ptr; IrInstruction *ptr;
}; };
struct IrInstructionWidenOrShorten {
IrInstruction base;
IrInstruction *target;
};
enum LValPurpose { enum LValPurpose {
LValPurposeNone, LValPurposeNone,
LValPurposeAssign, LValPurposeAssign,

View File

@ -973,9 +973,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
case CastOpIntToPtr: case CastOpIntToPtr:
return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, ""); return LLVMBuildIntToPtr(g->builder, expr_val, wanted_type->type_ref, "");
case CastOpWidenOrShorten:
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base),
actual_type, wanted_type, expr_val);
case CastOpResizeSlice: case CastOpResizeSlice:
{ {
assert(cast_instruction->tmp_ptr); assert(cast_instruction->tmp_ptr);
@ -1086,9 +1083,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
case CastOpIntToEnum: case CastOpIntToEnum:
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base), return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base),
actual_type, wanted_type->data.enumeration.tag_type, expr_val); actual_type, wanted_type->data.enumeration.tag_type, expr_val);
case CastOpEnumToInt:
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base),
actual_type->data.enumeration.tag_type, wanted_type, expr_val);
} }
zig_unreachable(); zig_unreachable();
} }
@ -1101,6 +1095,24 @@ static LLVMValueRef ir_render_pointer_reinterpret(CodeGen *g, IrExecutable *exec
return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, ""); return LLVMBuildBitCast(g->builder, ptr, wanted_type->type_ref, "");
} }
static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable,
IrInstructionWidenOrShorten *instruction)
{
TypeTableEntry *actual_type = instruction->target->value.type;
// TODO instead of this logic, use the Noop instruction to change the type from
// enum_tag to the underlying int type
TypeTableEntry *int_type;
if (actual_type->id == TypeTableEntryIdEnum) {
TypeTableEntry *tag_type = actual_type->data.enumeration.tag_type;
assert(tag_type->id == TypeTableEntryIdEnumTag);
int_type = tag_type->data.enum_tag.int_type;
} else {
int_type = actual_type;
}
LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base), int_type,
instruction->base.value.type, target_val);
}
static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable,
IrInstructionUnreachable *unreachable_instruction) IrInstructionUnreachable *unreachable_instruction)
@ -2309,6 +2321,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction); return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction);
case IrInstructionIdPointerReinterpret: case IrInstructionIdPointerReinterpret:
return ir_render_pointer_reinterpret(g, executable, (IrInstructionPointerReinterpret *)instruction); return ir_render_pointer_reinterpret(g, executable, (IrInstructionPointerReinterpret *)instruction);
case IrInstructionIdWidenOrShorten:
return ir_render_widen_or_shorten(g, executable, (IrInstructionWidenOrShorten *)instruction);
case IrInstructionIdSwitchVar: case IrInstructionIdSwitchVar:
zig_panic("TODO render switch var instruction to LLVM"); zig_panic("TODO render switch var instruction to LLVM");
case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitList:

View File

@ -455,6 +455,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionPointerReinterpr
return IrInstructionIdPointerReinterpret; return IrInstructionIdPointerReinterpret;
} }
static constexpr IrInstructionId ir_instruction_id(IrInstructionWidenOrShorten *) {
return IrInstructionIdWidenOrShorten;
}
template<typename T> template<typename T>
static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) { static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate<T>(1); T *special_instruction = allocate<T>(1);
@ -1883,6 +1887,18 @@ static IrInstruction *ir_build_pointer_reinterpret(IrBuilder *irb, Scope *scope,
return &instruction->base; return &instruction->base;
} }
static IrInstruction *ir_build_widen_or_shorten(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *target)
{
IrInstructionWidenOrShorten *instruction = ir_build_instruction<IrInstructionWidenOrShorten>(
irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target);
return &instruction->base;
}
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0; results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0; results[ReturnKindError] = 0;
@ -4638,7 +4654,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
case CastOpNoCast: case CastOpNoCast:
zig_unreachable(); zig_unreachable();
case CastOpNoop: case CastOpNoop:
case CastOpWidenOrShorten:
*const_val = *other_val; *const_val = *other_val;
const_val->type = new_type; const_val->type = new_type;
break; break;
@ -4684,10 +4699,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
const_val->special = ConstValSpecialStatic; const_val->special = ConstValSpecialStatic;
break; break;
} }
case CastOpEnumToInt:
bignum_init_unsigned(&const_val->data.x_bignum, other_val->data.x_enum.tag);
const_val->special = ConstValSpecialStatic;
break;
} }
} }
static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
@ -5181,6 +5192,50 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s
return result; return result;
} }
static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
assert(wanted_type->id == TypeTableEntryIdInt);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type, val->depends_on_compile_var);
init_const_unsigned_negative(&result->value, wanted_type, val->data.x_enum.tag, false);
return result;
}
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
source_instr->source_node, target);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
assert(wanted_type->id == TypeTableEntryIdInt || wanted_type->id == TypeTableEntryIdFloat);
if (instr_is_comptime(target)) {
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
source_instr->source_node, wanted_type, val->depends_on_compile_var);
result->value = *val;
result->value.type = wanted_type;
return result;
}
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
source_instr->source_node, target);
result->value.type = wanted_type;
return result;
}
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
TypeTableEntry *wanted_type, IrInstruction *value) TypeTableEntry *wanted_type, IrInstruction *value)
{ {
@ -5233,7 +5288,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
(wanted_type_canon->id == TypeTableEntryIdFloat && (wanted_type_canon->id == TypeTableEntryIdFloat &&
actual_type_canon->id == TypeTableEntryIdFloat)) actual_type_canon->id == TypeTableEntryIdFloat))
{ {
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpWidenOrShorten, false); return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
} }
// explicit cast from int to float // explicit cast from int to float
@ -5411,7 +5466,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
actual_type->id == TypeTableEntryIdEnum && actual_type->id == TypeTableEntryIdEnum &&
actual_type->data.enumeration.gen_field_count == 0) actual_type->data.enumeration.gen_field_count == 0)
{ {
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpEnumToInt, false); return ir_analyze_enum_to_int(ira, source_instr, value, wanted_type);
} }
// explicit cast from undefined to anything // explicit cast from undefined to anything
@ -9882,6 +9937,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
switch (instruction->id) { switch (instruction->id) {
case IrInstructionIdInvalid: case IrInstructionIdInvalid:
case IrInstructionIdPointerReinterpret: case IrInstructionIdPointerReinterpret:
case IrInstructionIdWidenOrShorten:
case IrInstructionIdStructInit: case IrInstructionIdStructInit:
case IrInstructionIdStructFieldPtr: case IrInstructionIdStructFieldPtr:
case IrInstructionIdEnumFieldPtr: case IrInstructionIdEnumFieldPtr:
@ -10188,6 +10244,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdTestComptime: case IrInstructionIdTestComptime:
case IrInstructionIdInitEnum: case IrInstructionIdInitEnum:
case IrInstructionIdPointerReinterpret: case IrInstructionIdPointerReinterpret:
case IrInstructionIdWidenOrShorten:
return false; return false;
case IrInstructionIdAsm: case IrInstructionIdAsm:
{ {

View File

@ -923,11 +923,17 @@ static void ir_print_init_enum(IrPrint *irp, IrInstructionInitEnum *instruction)
} }
static void ir_print_pointer_reinterpret(IrPrint *irp, IrInstructionPointerReinterpret *instruction) { static void ir_print_pointer_reinterpret(IrPrint *irp, IrInstructionPointerReinterpret *instruction) {
fprintf(irp->f, "(%s)(", buf_ptr(&instruction->base.value.type->name)); fprintf(irp->f, "@pointerReinterpret(");
ir_print_other_instruction(irp, instruction->ptr); ir_print_other_instruction(irp, instruction->ptr);
fprintf(irp->f, ")"); fprintf(irp->f, ")");
} }
static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) {
fprintf(irp->f, "@widenOrShorten(");
ir_print_other_instruction(irp, instruction->target);
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) {
@ -1170,6 +1176,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdPointerReinterpret: case IrInstructionIdPointerReinterpret:
ir_print_pointer_reinterpret(irp, (IrInstructionPointerReinterpret *)instruction); ir_print_pointer_reinterpret(irp, (IrInstructionPointerReinterpret *)instruction);
break; break;
case IrInstructionIdWidenOrShorten:
ir_print_widen_or_shorten(irp, (IrInstructionWidenOrShorten *)instruction);
break;
} }
fprintf(irp->f, "\n"); fprintf(irp->f, "\n");
} }

View File

@ -1,31 +0,0 @@
const assert = @import("std").debug.assert;
enum Number {
Zero,
One,
Two,
Three,
Four,
}
fn enumToInt() {
@setFnTest(this, true);
shouldEqual(false, Number.Zero, 0);
shouldEqual(false, Number.One, 1);
shouldEqual(false, Number.Two, 2);
shouldEqual(false, Number.Three, 3);
shouldEqual(false, Number.Four, 4);
shouldEqual(true, Number.Zero, 0);
shouldEqual(true, Number.One, 1);
shouldEqual(true, Number.Two, 2);
shouldEqual(true, Number.Three, 3);
shouldEqual(true, Number.Four, 4);
}
fn shouldEqual(inline static_eval: bool, n: Number, expected: usize) {
@setFnStaticEval(this, static_eval);
assert(usize(n) == expected);
}

View File

@ -73,6 +73,29 @@ const AnEnumWithPayload = enum {
const Number = enum {
Zero,
One,
Two,
Three,
Four,
};
fn enumToInt() {
@setFnTest(this);
shouldEqual(Number.Zero, 0);
shouldEqual(Number.One, 1);
shouldEqual(Number.Two, 2);
shouldEqual(Number.Three, 3);
shouldEqual(Number.Four, 4);
}
fn shouldEqual(n: Number, expected: usize) {
assert(usize(n) == expected);
}
// TODO import from std
fn assert(ok: bool) { fn assert(ok: bool) {
if (!ok) if (!ok)
@unreachable(); @unreachable();