authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-08 23:41:40-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-08 23:41:40-07:00
logb7dd88ad68aab5b6bc8321431d1a53b343b2dd37
tree78a9cd198eb94529a5841eb68c6bf57f9be21607
parent14b9cbd43c21ad2ba75b38ef5fc681c044e7662e

suport checked arithmetic operations via intrinsics

closes #32

10 files changed, 205 insertions(+), 19 deletions(-)

doc/langref.md+1-1
......@@ -160,7 +160,7 @@ SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression)
160160
161161PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const)))
162162
163PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType
163PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | (token(AtSign) token(Symbol) FnCallExpression)
164164
165165StructValueExpression : token(Type) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace)
166166
src/analyze.cpp+40-2
......@@ -2064,6 +2064,41 @@ static TypeTableEntry *analyze_compiler_fn_type(CodeGen *g, ImportTableEntry *im
20642064 }
20652065}
20662066
2067static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
2068 TypeTableEntry *expected_type, AstNode *node)
2069{
2070 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
2071 Buf *name = &fn_ref_expr->data.symbol;
2072
2073 auto entry = g->builtin_fn_table.maybe_get(name);
2074
2075 if (entry) {
2076 BuiltinFnEntry *builtin_fn = entry->value;
2077 int actual_param_count = node->data.fn_call_expr.params.length;
2078
2079 assert(node->codegen_node);
2080 node->codegen_node->data.fn_call_node.builtin_fn = builtin_fn;
2081
2082 if (builtin_fn->param_count != actual_param_count) {
2083 add_node_error(g, node,
2084 buf_sprintf("expected %d arguments, got %d",
2085 builtin_fn->param_count, actual_param_count));
2086 }
2087
2088 for (int i = 0; i < actual_param_count; i += 1) {
2089 AstNode *child = node->data.fn_call_expr.params.at(i);
2090 TypeTableEntry *expected_param_type = builtin_fn->param_types[i];
2091 analyze_expression(g, import, context, expected_param_type, child);
2092 }
2093
2094 return builtin_fn->return_type;
2095 } else {
2096 add_node_error(g, node,
2097 buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
2098 return g->builtin_types.entry_invalid;
2099 }
2100}
2101
20672102static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
20682103 TypeTableEntry *expected_type, AstNode *node)
20692104{
......@@ -2091,6 +2126,9 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
20912126 return g->builtin_types.entry_invalid;
20922127 }
20932128 } else if (fn_ref_expr->type == NodeTypeSymbol) {
2129 if (node->data.fn_call_expr.is_builtin) {
2130 return analyze_builtin_fn_call_expr(g, import, context, expected_type, node);
2131 }
20942132 name = &fn_ref_expr->data.symbol;
20952133 } else {
20962134 add_node_error(g, node,
......@@ -2126,12 +2164,12 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
21262164 if (fn_proto->is_var_args) {
21272165 if (actual_param_count < expected_param_count) {
21282166 add_node_error(g, node,
2129 buf_sprintf("wrong number of arguments. Expected at least %d, got %d.",
2167 buf_sprintf("expected at least %d arguments, got %d",
21302168 expected_param_count, actual_param_count));
21312169 }
21322170 } else if (expected_param_count != actual_param_count) {
21332171 add_node_error(g, node,
2134 buf_sprintf("wrong number of arguments. Expected %d, got %d.",
2172 buf_sprintf("expected %d arguments, got %d",
21352173 expected_param_count, actual_param_count));
21362174 }
21372175
src/analyze.hpp+20-7
......@@ -148,6 +148,20 @@ struct FnTableEntry {
148148 HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
149149};
150150
151enum BuiltinFnId {
152 BuiltinFnIdInvalid,
153 BuiltinFnIdArithmeticWithOverflow,
154};
155
156struct BuiltinFnEntry {
157 BuiltinFnId id;
158 Buf name;
159 int param_count;
160 TypeTableEntry *return_type;
161 TypeTableEntry **param_types;
162 LLVMValueRef fn_val;
163};
164
151165struct CodeGen {
152166 LLVMModuleRef module;
153167 ZigList<ErrorMsg*> errors;
......@@ -161,6 +175,7 @@ struct CodeGen {
161175 HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table;
162176 HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table;
163177 HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
178 HashMap<Buf *, BuiltinFnEntry *, buf_hash, buf_eql_buf> builtin_fn_table;
164179
165180 struct {
166181 TypeTableEntry *entry_bool;
......@@ -342,6 +357,10 @@ struct WhileNode {
342357 bool contains_break;
343358};
344359
360struct FnCallNode {
361 BuiltinFnEntry *builtin_fn;
362};
363
345364struct CodeGenNode {
346365 union {
347366 TypeNode type_node; // for NodeTypeType
......@@ -363,17 +382,11 @@ struct CodeGenNode {
363382 ParamDeclNode param_decl_node; // for NodeTypeParamDecl
364383 ImportNode import_node; // for NodeTypeUse
365384 WhileNode while_node; // for NodeTypeWhileExpr
385 FnCallNode fn_call_node; // for NodeTypeFnCallExpr
366386 } data;
367387 ExprNode expr_node; // for all the expression nodes
368388};
369389
370static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) {
371 // Assume that the expression evaluates to a simple name and return the buf
372 // TODO after type checking works we should be able to remove this hack
373 assert(node->type == NodeTypeSymbol);
374 return &node->data.symbol;
375}
376
377390void semantic_analyze(CodeGen *g);
378391void add_node_error(CodeGen *g, AstNode *node, Buf *msg);
379392void alloc_codegen_node(AstNode *node);
src/codegen.cpp+105-2
......@@ -22,6 +22,7 @@ CodeGen *codegen_create(Buf *root_source_dir) {
2222 g->str_table.init(32);
2323 g->link_table.init(32);
2424 g->import_table.init(32);
25 g->builtin_fn_table.init(32);
2526 g->build_type = CodeGenBuildTypeDebug;
2627 g->root_source_dir = root_source_dir;
2728
......@@ -139,6 +140,41 @@ static TypeTableEntry *get_expr_type(AstNode *node) {
139140 return cast_type ? cast_type : node->codegen_node->expr_node.type_entry;
140141}
141142
143static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
144 assert(node->type == NodeTypeFnCallExpr);
145 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
146 assert(fn_ref_expr->type == NodeTypeSymbol);
147 BuiltinFnEntry *builtin_fn = node->codegen_node->data.fn_call_node.builtin_fn;
148
149 switch (builtin_fn->id) {
150 case BuiltinFnIdInvalid:
151 zig_unreachable();
152 case BuiltinFnIdArithmeticWithOverflow:
153 {
154 int fn_call_param_count = node->data.fn_call_expr.params.length;
155 assert(fn_call_param_count == 3);
156
157 LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(0));
158 LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(1));
159 LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(2));
160
161 LLVMValueRef params[] = {
162 op1,
163 op2,
164 };
165
166 add_debug_source_node(g, node);
167 LLVMValueRef result_struct = LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 2, "");
168 LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, "");
169 LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, "");
170 LLVMBuildStore(g->builder, result, ptr_result);
171
172 return overflow_bit;
173 }
174 }
175 zig_unreachable();
176}
177
142178static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
143179 assert(node->type == NodeTypeFnCallExpr);
144180
......@@ -159,7 +195,15 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
159195 zig_unreachable();
160196 }
161197 } else if (fn_ref_expr->type == NodeTypeSymbol) {
162 Buf *name = hack_get_fn_call_name(g, fn_ref_expr);
198 if (node->data.fn_call_expr.is_builtin) {
199 return gen_builtin_fn_call_expr(g, node);
200 }
201
202 // Assume that the expression evaluates to a simple name and return the buf
203 // TODO after we support function pointers we can make this generic
204 assert(fn_ref_expr->type == NodeTypeSymbol);
205 Buf *name = &fn_ref_expr->data.symbol;
206
163207 struct_type = nullptr;
164208 first_param_expr = nullptr;
165209 fn_table_entry = g->cur_fn->import_entry->fn_table.get(name);
......@@ -2167,6 +2211,64 @@ static void define_builtin_types(CodeGen *g) {
21672211 }
21682212}
21692213
2214static void define_builtin_fns_int(CodeGen *g, TypeTableEntry *type_entry) {
2215 assert(type_entry->id == TypeTableEntryIdInt);
2216 struct OverflowFn {
2217 const char *bare_name;
2218 const char *signed_name;
2219 const char *unsigned_name;
2220 };
2221 OverflowFn overflow_fns[] = {
2222 {"add", "sadd", "uadd"},
2223 {"sub", "ssub", "usub"},
2224 {"mul", "smul", "umul"},
2225 };
2226 for (int i = 0; i < sizeof(overflow_fns)/sizeof(overflow_fns[0]); i += 1) {
2227 OverflowFn *overflow_fn = &overflow_fns[i];
2228 BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
2229 buf_resize(&builtin_fn->name, 0);
2230 buf_appendf(&builtin_fn->name, "%s_with_overflow_%s", overflow_fn->bare_name, buf_ptr(&type_entry->name));
2231 builtin_fn->id = BuiltinFnIdArithmeticWithOverflow;
2232 builtin_fn->return_type = g->builtin_types.entry_bool;
2233 builtin_fn->param_count = 3;
2234 builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
2235 builtin_fn->param_types[0] = type_entry;
2236 builtin_fn->param_types[1] = type_entry;
2237 builtin_fn->param_types[2] = get_pointer_to_type(g, type_entry, false, false);
2238
2239
2240 const char *signed_str = type_entry->data.integral.is_signed ?
2241 overflow_fn->signed_name : overflow_fn->unsigned_name;
2242 Buf *llvm_name = buf_sprintf("llvm.%s.with.overflow.i%" PRIu64, signed_str, type_entry->size_in_bits);
2243
2244 LLVMTypeRef return_elem_types[] = {
2245 type_entry->type_ref,
2246 LLVMInt1Type(),
2247 };
2248 LLVMTypeRef param_types[] = {
2249 type_entry->type_ref,
2250 type_entry->type_ref,
2251 };
2252 LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false);
2253 LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false);
2254 builtin_fn->fn_val = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type);
2255 assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
2256
2257 g->builtin_fn_table.put(&builtin_fn->name, builtin_fn);
2258 }
2259}
2260
2261static void define_builtin_fns(CodeGen *g) {
2262 define_builtin_fns_int(g, g->builtin_types.entry_u8);
2263 define_builtin_fns_int(g, g->builtin_types.entry_u16);
2264 define_builtin_fns_int(g, g->builtin_types.entry_u32);
2265 define_builtin_fns_int(g, g->builtin_types.entry_u64);
2266 define_builtin_fns_int(g, g->builtin_types.entry_i8);
2267 define_builtin_fns_int(g, g->builtin_types.entry_i16);
2268 define_builtin_fns_int(g, g->builtin_types.entry_i32);
2269 define_builtin_fns_int(g, g->builtin_types.entry_i64);
2270}
2271
21702272
21712273
21722274static void init(CodeGen *g, Buf *source_path) {
......@@ -2228,9 +2330,10 @@ static void init(CodeGen *g, Buf *source_path) {
22282330 "", 0, !g->strip_debug_symbols);
22292331
22302332 // This is for debug stuff that doesn't have a real file.
2231 g->dummy_di_file = nullptr; //LLVMZigCreateFile(g->dbuilder, "", "");
2333 g->dummy_di_file = nullptr;
22322334
22332335 define_builtin_types(g);
2336 define_builtin_fns(g);
22342337
22352338}
22362339
src/parser.cpp+13-1
......@@ -1313,7 +1313,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) {
13131313}
13141314
13151315/*
1316PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType
1316PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | (token(AtSign) token(Symbol) FnCallExpression)
13171317KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null)
13181318*/
13191319static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
......@@ -1356,6 +1356,18 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
13561356 AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token);
13571357 *token_index += 1;
13581358 return node;
1359 } else if (token->id == TokenIdAtSign) {
1360 *token_index += 1;
1361 Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
1362 AstNode *name_node = ast_create_node(pc, NodeTypeSymbol, name_tok);
1363 ast_buf_from_token(pc, name_tok, &name_node->data.symbol);
1364
1365 AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token);
1366 node->data.fn_call_expr.fn_ref_expr = name_node;
1367 ast_eat_token(pc, token_index, TokenIdLParen);
1368 ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params);
1369 node->data.fn_call_expr.is_builtin = true;
1370 return node;
13591371 } else if (token->id == TokenIdSymbol) {
13601372 Token *next_token = &pc->tokens->at(*token_index + 1);
13611373
src/parser.hpp+1
......@@ -176,6 +176,7 @@ struct AstNodeBinOpExpr {
176176struct AstNodeFnCallExpr {
177177 AstNode *fn_ref_expr;
178178 ZigList<AstNode *> params;
179 bool is_builtin;
179180};
180181
181182struct AstNodeArrayAccessExpr {
src/tokenizer.cpp+5
......@@ -376,6 +376,10 @@ void tokenize(Buf *buf, Tokenization *out) {
376376 begin_token(&t, TokenIdTilde);
377377 end_token(&t);
378378 break;
379 case '@':
380 begin_token(&t, TokenIdAtSign);
381 end_token(&t);
382 break;
379383 case '-':
380384 begin_token(&t, TokenIdDash);
381385 t.state = TokenizeStateSawDash;
......@@ -1074,6 +1078,7 @@ static const char * token_name(Token *token) {
10741078 case TokenIdMaybe: return "Maybe";
10751079 case TokenIdDoubleQuestion: return "DoubleQuestion";
10761080 case TokenIdMaybeAssign: return "MaybeAssign";
1081 case TokenIdAtSign: return "AtSign";
10771082 }
10781083 return "(invalid token)";
10791084}
src/tokenizer.hpp+1
......@@ -89,6 +89,7 @@ enum TokenId {
8989 TokenIdMaybe,
9090 TokenIdDoubleQuestion,
9191 TokenIdMaybeAssign,
92 TokenIdAtSign,
9293};
9394
9495struct Token {
std/std.zig-5
......@@ -63,10 +63,6 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool {
6363 return true;
6464 }
6565
66 x *= radix;
67 x += digit;
68
69 /* TODO intrinsics mul and add with overflow
7066 // x *= radix
7167 if (@mul_with_overflow_u64(x, radix, &x)) {
7268 return true;
......@@ -76,7 +72,6 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool {
7672 if (@add_with_overflow_u64(x, digit, &x)) {
7773 return true;
7874 }
79 */
8075
8176 i += 1;
8277 }
test/run_tests.cpp+19-1
......@@ -953,6 +953,24 @@ fn f(c: u8) -> u8 {
953953 } else {
954954 2
955955 }
956}
957 )SOURCE", "OK\n");
958
959 add_simple_case("overflow intrinsics", R"SOURCE(
960use "std.zig";
961pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
962 var result: u8;
963 if (!@add_with_overflow_u8(250, 100, &result)) {
964 print_str("BAD\n");
965 }
966 if (@add_with_overflow_u8(100, 150, &result)) {
967 print_str("BAD\n");
968 }
969 if (result != 250) {
970 print_str("BAD\n");
971 }
972 print_str("OK\n");
973 return 0;
956974}
957975 )SOURCE", "OK\n");
958976}
......@@ -995,7 +1013,7 @@ fn a() {
9951013 b(1);
9961014}
9971015fn b(a: i32, b: i32, c: i32) { }
998 )SOURCE", 1, ".tmp_source.zig:3:6: error: wrong number of arguments. Expected 3, got 1.");
1016 )SOURCE", 1, ".tmp_source.zig:3:6: error: expected 3 arguments, got 1");
9991017
10001018 add_compile_fail_case("invalid type", R"SOURCE(
10011019fn a() -> bogus {}