From b09a0cd0727055217742cb178822521733078c27 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Jan 2016 15:23:24 -0700 Subject: [PATCH] allow constants to have number literal values also codegen takes advantage of constant expr eval --- src/all_types.hpp | 2 + src/analyze.cpp | 60 +++++++++++++++-- src/codegen.cpp | 159 +++++++++++++++++++++++++++++++++------------ test/run_tests.cpp | 2 +- 4 files changed, 176 insertions(+), 47 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 4c0dab772..24d4011bc 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -90,6 +90,7 @@ struct Expr { Cast implicit_cast; // happens first Cast implicit_maybe_cast; // happens second + LLVMValueRef const_llvm_val; ConstExprValue const_val; }; @@ -955,6 +956,7 @@ struct CodeGen { // there will not be a corresponding fn_defs entry. ZigList fn_protos; ZigList global_vars; + ZigList global_const_list; OutType out_type; FnTableEntry *cur_fn; diff --git a/src/analyze.cpp b/src/analyze.cpp index 9a9de850a..1325574b0 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1667,7 +1667,13 @@ static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, static TypeTableEntry *resolve_expr_const_val_as_other_expr(CodeGen *g, AstNode *node, AstNode *other) { Expr *expr = get_resolved_expr(node); Expr *other_expr = get_resolved_expr(other); - expr->const_val = other_expr->const_val; + ConstExprValue *other_const_val; + if (other_expr->implicit_maybe_cast.after_type) { + other_const_val = &other_expr->implicit_maybe_cast.const_val; + } else { + other_const_val = &other_expr->const_val; + } + expr->const_val = *other_const_val; return other_expr->type_entry; } @@ -1766,8 +1772,14 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, AstNode *decl_node = var->decl_node; if (decl_node->type == NodeTypeVariableDeclaration) { AstNode *expr_node = decl_node->data.variable_declaration.expr; - ConstExprValue *const_val = &get_resolved_expr(expr_node)->const_val; - if (const_val->ok) { + Expr *other_expr = get_resolved_expr(expr_node); + ConstExprValue *other_const_val; + if (other_expr->implicit_maybe_cast.after_type) { + other_const_val = &other_expr->implicit_maybe_cast.const_val; + } else { + other_const_val = &other_expr->const_val; + } + if (other_const_val->ok) { return resolve_expr_const_val_as_other_expr(g, node, expr_node); } } @@ -2206,6 +2218,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa bool expr_is_maybe) { bool is_const = variable_declaration->is_const; + bool is_export = (variable_declaration->visib_mod == VisibModExport); TypeTableEntry *explicit_type = nullptr; if (variable_declaration->type != nullptr) { @@ -2233,7 +2246,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable")); implicit_type = g->builtin_types.entry_invalid; - } else if (!is_const && + } else if ((!is_const || is_export) && (implicit_type->id == TypeTableEntryIdNumLitFloat || implicit_type->id == TypeTableEntryIdNumLitInt)) { @@ -3273,6 +3286,33 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, return g->builtin_types.entry_unreachable; } +static bool type_has_codegen_value(TypeTableEntryId id) { + switch (id) { + case TypeTableEntryIdInvalid: + case TypeTableEntryIdMetaType: + case TypeTableEntryIdVoid: + case TypeTableEntryIdUnreachable: + return false; + + // TODO make num lits return false when we make implicit casts insert ast nodes + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + + case TypeTableEntryIdBool: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdPointer: + case TypeTableEntryIdArray: + case TypeTableEntryIdStruct: + case TypeTableEntryIdMaybe: + case TypeTableEntryIdError: + case TypeTableEntryIdEnum: + case TypeTableEntryIdFn: + return true; + } + zig_unreachable(); +} + static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -3457,14 +3497,22 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, resolve_type_compatibility(g, context, node, expected_type, return_type); Expr *expr = get_resolved_expr(node); - expr->type_entry = return_type; + if (!expr->resolved_type) { + expr->resolved_type = return_type; + } + expr->type_entry = expr->resolved_type; expr->block_context = context; + if (expr->const_val.ok && type_has_codegen_value(expr->resolved_type->id)) { + g->global_const_list.append(expr); + } + + if (expr->type_entry->id == TypeTableEntryIdUnreachable) { return expr->type_entry; } - /* + /* TODO delete this code when we make implicit casts insert ast nodes Cast *cast_node = &expr->implicit_cast; if (cast_node->after_type) { eval_const_expr_implicit_cast(g, import, context, node, cast_node, node); diff --git a/src/codegen.cpp b/src/codegen.cpp index be4661c57..8e9ebe97a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -134,38 +134,6 @@ static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_no } } -static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *expr_node) { - Expr *expr = get_resolved_expr(expr_node); - TypeTableEntry *type_entry = expr->resolved_type; - if (!type_entry) { - type_entry = expr->type_entry; - } - assert(type_entry); - - ConstExprValue *const_val = &expr->const_val; - - assert(const_val->ok); - - if (type_entry->id == TypeTableEntryIdInt) { - assert(const_val->data.x_bignum.kind == BigNumKindInt); - return LLVMConstInt(type_entry->type_ref, - bignum_to_twos_complement(&const_val->data.x_bignum), - type_entry->data.integral.is_signed); - } else if (type_entry->id == TypeTableEntryIdFloat) { - if (const_val->data.x_bignum.kind == BigNumKindFloat) { - return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float); - } else { - int64_t x = const_val->data.x_bignum.data.x_uint; - if (const_val->data.x_bignum.is_negative) { - x = -x; - } - return LLVMConstReal(type_entry->type_ref, x); - } - } else { - zig_unreachable(); - } -} - static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; @@ -277,7 +245,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { case BuiltinFnIdMinValue: case BuiltinFnIdMaxValue: case BuiltinFnIdMemberCount: - return gen_number_literal(g, node); + // caught by constant expression eval codegen + zig_unreachable(); } zig_unreachable(); } @@ -1884,6 +1853,16 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa } static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) { + AstNode *init_expr = node->data.variable_declaration.expr; + if (node->data.variable_declaration.is_const && init_expr) { + TypeTableEntry *init_expr_type = get_expr_type(init_expr); + if (init_expr_type->id == TypeTableEntryIdNumLitFloat || + init_expr_type->id == TypeTableEntryIdNumLitInt) + { + return nullptr; + } + } + LLVMValueRef init_val; return gen_var_decl_raw(g, node, &node->data.variable_declaration, get_resolved_expr(node)->block_context, false, &init_val); @@ -1992,6 +1971,11 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) { } static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { + Expr *expr = get_resolved_expr(node); + if (expr->const_val.ok) { + assert(expr->const_llvm_val); + return expr->const_llvm_val; + } switch (node->type) { case NodeTypeBinOpExpr: return gen_bin_op_expr(g, node); @@ -2009,11 +1993,6 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { return gen_slice_expr(g, node); case NodeTypeFieldAccessExpr: return gen_field_access_expr(g, node, false); - case NodeTypeBoolLiteral: - if (node->data.bool_literal.value) - return LLVMConstAllOnes(LLVMInt1Type()); - else - return LLVMConstNull(LLVMInt1Type()); case NodeTypeNullLiteral: return gen_null_literal(g, node); case NodeTypeIfBoolExpr: @@ -2026,8 +2005,6 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { return gen_for_expr(g, node); case NodeTypeAsmExpr: return gen_asm_expr(g, node); - case NodeTypeNumberLiteral: - return gen_number_literal(g, node); case NodeTypeErrorLiteral: return gen_error_literal(g, node); case NodeTypeStringLiteral: @@ -2070,6 +2047,10 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { return gen_container_init_expr(g, node); case NodeTypeSwitchExpr: return gen_switch_expr(g, node); + case NodeTypeNumberLiteral: + case NodeTypeBoolLiteral: + // caught by constant expression eval codegen + zig_unreachable(); case NodeTypeRoot: case NodeTypeRootExportDecl: case NodeTypeFnProto: @@ -2129,16 +2110,114 @@ static void build_label_blocks(CodeGen *g, AstNode *block_node) { label_node->data.label.label_entry->basic_block = LLVMAppendBasicBlock( g->cur_fn->fn_value, buf_ptr(name)); } +} +static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val) { + assert(const_val->ok); + + if (type_entry->id == TypeTableEntryIdInt) { + return LLVMConstInt(type_entry->type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false); + } else if (type_entry->id == TypeTableEntryIdFloat) { + if (const_val->data.x_bignum.kind == BigNumKindFloat) { + return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float); + } else { + int64_t x = const_val->data.x_bignum.data.x_uint; + if (const_val->data.x_bignum.is_negative) { + x = -x; + } + return LLVMConstReal(type_entry->type_ref, x); + } + } else if (type_entry->id == TypeTableEntryIdBool) { + if (const_val->data.x_bool) { + return LLVMConstAllOnes(LLVMInt1Type()); + } else { + return LLVMConstNull(LLVMInt1Type()); + } + } else if (type_entry->id == TypeTableEntryIdMaybe) { + TypeTableEntry *child_type = type_entry->data.maybe.child_type; + LLVMValueRef child_val; + LLVMValueRef maybe_val; + if (const_val->data.x_maybe) { + child_val = gen_const_val(g, child_type, const_val->data.x_maybe); + maybe_val = LLVMConstAllOnes(LLVMInt1Type()); + } else { + child_val = LLVMConstNull(child_type->type_ref); + maybe_val = LLVMConstNull(LLVMInt1Type()); + } + LLVMValueRef fields[] = { + child_val, + maybe_val, + }; + return LLVMConstStruct(fields, 2, false); + } else if (type_entry->id == TypeTableEntryIdStruct) { + zig_panic("TODO"); + } else if (type_entry->id == TypeTableEntryIdArray) { + zig_panic("TODO"); + } else if (type_entry->id == TypeTableEntryIdEnum) { + LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref; + LLVMValueRef tag_value = LLVMConstInt(tag_type_ref, const_val->data.x_enum.tag, false); + if (type_entry->data.enumeration.gen_field_count == 0) { + return tag_value; + } else { + zig_panic("TODO"); + /* + LLVMValueRef fields[] = { + tag_value, + union_value, + }; + return LLVMConstStruct(fields, 2, false); + */ + } + } else if (type_entry->id == TypeTableEntryIdFn) { + return const_val->data.x_fn->fn_value; + } else { + zig_unreachable(); + } +} + +static void gen_const_globals(CodeGen *g) { + for (int i = 0; i < g->global_const_list.length; i += 1) { + Expr *expr = g->global_const_list.at(i); + ConstExprValue *const_val = &expr->const_val; + assert(const_val->ok); + TypeTableEntry *type_entry = expr->resolved_type; + + // TODO delete this if when we make implicit casts insert ast nodes + if (type_entry->id == TypeTableEntryIdNumLitFloat || + type_entry->id == TypeTableEntryIdNumLitInt) + { + continue; + } + + if (handle_is_ptr(type_entry)) { + LLVMValueRef global_value = LLVMAddGlobal(g->module, type_entry->type_ref, ""); + LLVMSetLinkage(global_value, LLVMPrivateLinkage); + LLVMValueRef init_val = gen_const_val(g, type_entry, const_val); + LLVMSetInitializer(global_value, init_val); + LLVMSetGlobalConstant(global_value, true); + LLVMSetUnnamedAddr(global_value, true); + expr->const_llvm_val = global_value; + } else { + expr->const_llvm_val = gen_const_val(g, type_entry, const_val); + } + } } static void do_code_gen(CodeGen *g) { assert(!g->errors.length); + gen_const_globals(g); + // Generate module level variables for (int i = 0; i < g->global_vars.length; i += 1) { VariableTableEntry *var = g->global_vars.at(i); + if (var->type->id == TypeTableEntryIdNumLitFloat || + var->type->id == TypeTableEntryIdNumLitInt) + { + continue; + } + // TODO if the global is exported, set external linkage LLVMValueRef global_value = LLVMAddGlobal(g->module, var->type->type_ref, ""); LLVMSetLinkage(global_value, LLVMPrivateLinkage); diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 1e7da9068..1c6a78d81 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -866,7 +866,7 @@ pub fn main(args: [][]u8) i32 => { } )SOURCE", "20\n"); - add_simple_case("#min_value() and #max_value()", R"SOURCE( + add_simple_case("@min_value() and @max_value()", R"SOURCE( import "std.zig"; pub fn main(args: [][]u8) i32 => { print_str("max u8: ");