diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index b07b75601..9404010da 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -3550,7 +3550,8 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { case .Add, .Sub, .Not, .Xor, - .And: + .And, + .Mul_Mul: op := advance_token(p) expr := parse_unary_expr(p, lhs) diff --git a/core/odin/tokenizer/token.odin b/core/odin/tokenizer/token.odin index 48d08f127..8c84ca69b 100644 --- a/core/odin/tokenizer/token.odin +++ b/core/odin/tokenizer/token.odin @@ -67,6 +67,8 @@ Token_Kind :: enum u32 { Cmp_And, // && Cmp_Or, // || + Mul_Mul, // ** + B_Assign_Op_Begin, Add_Eq, // += Sub_Eq, // -= @@ -202,6 +204,8 @@ tokens := [Token_Kind.COUNT]string { "&&", "||", + "**", + "", "+=", "-=", diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index f4d50b36c..df9d5b01d 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -679,6 +679,9 @@ scan :: proc(t: ^Tokenizer) -> Token { if t.ch == '=' { advance_rune(t) kind = .Mul_Eq + } else if t.ch == '*' { + advance_rune(t) + kind = .Mul_Mul } case '=': kind = .Eq diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 584264f10..00b22e84b 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1173,7 +1173,7 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan // masked_load (ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T // masked_store(ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) - // masked_expand_load (ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T + // masked_expand_load (ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T // masked_compress_store(ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) Operand ptr = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7afb7808c..39c47ae03 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2950,6 +2950,43 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * return; } + + case Token_MulMul: { // 'expand_values' operator + if (!o->type) { + return; + } + + Type *type = base_type(o->type); + if (!is_type_struct(type) && !is_type_array(type)) { + gbString type_str = type_to_string(o->type); + error(node, "Expected a struct or array type to 'expand_values', got '%s'", type_str); + gb_string_free(type_str); + return; + } + + Type *tuple = alloc_type_tuple(); + + if (is_type_struct(type)) { + isize variable_count = type->Struct.fields.count; + tuple->Tuple.variables = permanent_slice_make(variable_count); + // NOTE(bill): don't copy the entities, this should be good enough + gb_memmove_array(tuple->Tuple.variables.data, type->Struct.fields.data, variable_count); + } else if (is_type_array(type)) { + isize variable_count = cast(isize)type->Array.count; + tuple->Tuple.variables = permanent_slice_make(variable_count); + for (isize i = 0; i < variable_count; i++) { + tuple->Tuple.variables[i] = alloc_entity_array_elem(nullptr, blank_token, type->Array.elem, cast(i32)i); + } + } + o->type = tuple; + o->mode = Addressing_Value; + + if (tuple->Tuple.variables.count == 1) { + o->type = tuple->Tuple.variables[0]->type; + } + + return; + } } if (!check_unary_op(c, o, op)) { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 71a458383..554ac79b7 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1,5 +1,6 @@ gb_internal lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise); gb_internal lbValue lb_build_slice_expr_value(lbProcedure *p, Ast *expr); +gb_internal lbValue lb_expand_values(lbProcedure *p, lbValue val, Type *type); gb_internal LLVMValueRef lb_const_low_bits_mask(LLVMTypeRef type, u64 bit_count) { GB_ASSERT(bit_count <= 64); @@ -4668,6 +4669,11 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { switch (ue->op.kind) { case Token_And: return lb_build_unary_and(p, expr); + case Token_MulMul: + { + lbValue val = lb_build_expr(p, ue->expr); + return lb_expand_values(p, val, type); + } default: { lbValue v = lb_build_expr(p, ue->expr); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 606ef9c29..e26bac5c6 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1324,6 +1324,48 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c return result; } +gb_internal lbValue lb_expand_values(lbProcedure *p, lbValue val, Type *type) { + Type *t = base_type(val.type); + + if (!is_type_tuple(type)) { + if (t->kind == Type_Struct) { + GB_ASSERT(t->Struct.fields.count == 1); + return lb_emit_struct_ev(p, val, 0); + } else if (t->kind == Type_Array) { + GB_ASSERT(t->Array.count == 1); + return lb_emit_struct_ev(p, val, 0); + } else { + GB_PANIC("Unknown type of expand_values"); + } + + } + + GB_ASSERT(is_type_tuple(type)); + // NOTE(bill): Doesn't need to be zero because it will be initialized in the loops + lbValue tuple = lb_addr_get_ptr(p, lb_add_local_generated(p, type, false)); + if (t->kind == Type_Struct) { + for_array(src_index, t->Struct.fields) { + Entity *field = t->Struct.fields[src_index]; + i32 field_index = field->Variable.field_index; + lbValue f = lb_emit_struct_ev(p, val, field_index); + lbValue ep = lb_emit_struct_ep(p, tuple, cast(i32)src_index); + lb_emit_store(p, ep, f); + } + } else if (is_type_array_like(t)) { + // TODO(bill): Clean-up this code + lbValue ap = lb_address_from_load_or_generate_local(p, val); + i32 n = cast(i32)get_array_type_count(t); + for (i32 i = 0; i < n; i++) { + lbValue f = lb_emit_load(p, lb_emit_array_epi(p, ap, i)); + lbValue ep = lb_emit_struct_ep(p, tuple, i); + lb_emit_store(p, ep, f); + } + } else { + GB_PANIC("Unknown type of expand_values"); + } + return lb_emit_load(p, tuple); +} + gb_internal LLVMValueRef llvm_splat_int(i64 count, LLVMTypeRef type, i64 value, bool is_signed=false) { LLVMValueRef v = LLVMConstInt(type, value, is_signed); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); @@ -3054,45 +3096,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu case BuiltinProc_expand_values: { lbValue val = lb_build_expr(p, ce->args[0]); - Type *t = base_type(val.type); - - if (!is_type_tuple(tv.type)) { - if (t->kind == Type_Struct) { - GB_ASSERT(t->Struct.fields.count == 1); - return lb_emit_struct_ev(p, val, 0); - } else if (t->kind == Type_Array) { - GB_ASSERT(t->Array.count == 1); - return lb_emit_struct_ev(p, val, 0); - } else { - GB_PANIC("Unknown type of expand_values"); - } - - } - - GB_ASSERT(is_type_tuple(tv.type)); - // NOTE(bill): Doesn't need to be zero because it will be initialized in the loops - lbValue tuple = lb_addr_get_ptr(p, lb_add_local_generated(p, tv.type, false)); - if (t->kind == Type_Struct) { - for_array(src_index, t->Struct.fields) { - Entity *field = t->Struct.fields[src_index]; - i32 field_index = field->Variable.field_index; - lbValue f = lb_emit_struct_ev(p, val, field_index); - lbValue ep = lb_emit_struct_ep(p, tuple, cast(i32)src_index); - lb_emit_store(p, ep, f); - } - } else if (is_type_array_like(t)) { - // TODO(bill): Clean-up this code - lbValue ap = lb_address_from_load_or_generate_local(p, val); - i32 n = cast(i32)get_array_type_count(t); - for (i32 i = 0; i < n; i++) { - lbValue f = lb_emit_load(p, lb_emit_array_epi(p, ap, i)); - lbValue ep = lb_emit_struct_ep(p, tuple, i); - lb_emit_store(p, ep, f); - } - } else { - GB_PANIC("Unknown type of expand_values"); - } - return lb_emit_load(p, tuple); + return lb_expand_values(p, val, tv.type); } case BuiltinProc_compress_values: { diff --git a/src/parser.cpp b/src/parser.cpp index 26276b20c..60e5ac9e1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3474,6 +3474,7 @@ gb_internal Ast *parse_unary_expr(AstFile *f, bool lhs) { case Token_Xor: case Token_And: case Token_Not: + case Token_MulMul: // 'expand_values' operator case Token_Mul: // Used for error handling when people do C-like things { Token token = advance_token(f); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 2bdd15a60..f505b142e 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -35,6 +35,7 @@ TOKEN_KIND(Token__OperatorBegin, ""), \ TOKEN_KIND(Token_Shr, ">>"), \ TOKEN_KIND(Token_CmpAnd, "&&"), \ TOKEN_KIND(Token_CmpOr, "||"), \ + TOKEN_KIND(Token_MulMul, "**"), \ \ TOKEN_KIND(Token__AssignOpBegin, ""), \ TOKEN_KIND(Token_AddEq, "+="), \ @@ -879,6 +880,9 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { if (t->curr_rune == '=') { advance_to_next_rune(t); token->kind = Token_MulEq; + } else if (t->curr_rune == '*') { + advance_to_next_rune(t); + token->kind = Token_MulMul; } break; case '=':