kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 4e76fca39ef26ce1dee46abb8952f1a57ebd111a
parent f12acfbd50f35de7c87bfefce0ca26709fb0b8a2
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 13 May 2026 15:08:02 -0700

Expand toy CG API coverage

Diffstat:
Mlang/toy/toy.c | 662+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Atest/toy/cases/21_cg_api_scalar_type_queries.expected | 1+
Atest/toy/cases/21_cg_api_scalar_type_queries.toy | 11+++++++++++
Atest/toy/cases/22_cg_api_typed_atomics_bits.expected | 1+
Atest/toy/cases/22_cg_api_typed_atomics_bits.toy | 8++++++++
Atest/toy/cases/23_cg_api_typed_varargs.expected | 1+
Atest/toy/cases/23_cg_api_typed_varargs.toy | 14++++++++++++++
7 files changed, 638 insertions(+), 60 deletions(-)

diff --git a/lang/toy/toy.c b/lang/toy/toy.c @@ -4,6 +4,7 @@ #include <stdarg.h> #include <stddef.h> #include <stdint.h> +#include <stdlib.h> #include <string.h> /* Public CG API coverage goals for this frontend are tracked in @@ -27,6 +28,7 @@ typedef enum ToyTokenKind { TOK_RETURN, TOK_TAIL, TOK_TYPE, + TOK_AS, TOK_INT, TOK_IDENT, TOK_NUMBER, @@ -72,6 +74,8 @@ typedef struct ToyToken { const uint8_t* text; size_t text_len; int64_t int_value; + double float_value; + int is_float; } ToyToken; typedef struct ToyLexer { @@ -125,6 +129,8 @@ static ToyToken toy_lexer_emit(ToyLexer* lex, ToyTokenKind kind, tok.text = start; tok.text_len = (size_t)(lex->cur - start); tok.int_value = 0; + tok.float_value = 0.0; + tok.is_float = 0; return tok; } @@ -142,6 +148,8 @@ static ToyToken toy_lexer_next(ToyLexer* lex) { tok.text = start; tok.text_len = 0; tok.int_value = 0; + tok.float_value = 0.0; + tok.is_float = 0; return tok; } @@ -239,12 +247,28 @@ static ToyToken toy_lexer_next(ToyLexer* lex) { if (toy_is_digit(c)) { int64_t v = (int64_t)(c - '0'); + int is_float = 0; while (lex->cur < lex->end && toy_is_digit(*lex->cur)) { v = v * 10 + (int64_t)(*lex->cur - '0'); lex->cur++; } + if (lex->cur < lex->end && *lex->cur == '.' && + lex->cur + 1 < lex->end && toy_is_digit(lex->cur[1])) { + is_float = 1; + lex->cur++; + while (lex->cur < lex->end && toy_is_digit(*lex->cur)) lex->cur++; + } tok = toy_lexer_emit(lex, TOK_NUMBER, start); tok.int_value = v; + tok.is_float = is_float; + if (is_float) { + char buf[64]; + size_t len = tok.text_len; + if (len >= sizeof buf) len = sizeof buf - 1; + memcpy(buf, tok.text, len); + buf[len] = '\0'; + tok.float_value = strtod(buf, NULL); + } return tok; } @@ -254,6 +278,8 @@ static ToyToken toy_lexer_next(ToyLexer* lex) { ToyTokenKind kind = TOK_IDENT; if (len == 2 && start[0] == 'f' && start[1] == 'n') kind = TOK_FN; + else if (len == 2 && start[0] == 'a' && start[1] == 's') + kind = TOK_AS; else if (len == 3 && start[0] == 'v' && start[1] == 'a' && start[2] == 'r') kind = TOK_VAR; else if (len == 3 && start[0] == 'i' && start[1] == 'n' && start[2] == 't') @@ -496,13 +522,73 @@ static CfreeCgSym toy_find_decl_sym(ToyParser* p, CfreeSym name) { * ============================================================ */ static CfreeCgTypeId toy_parse_type(ToyParser* p) { + if (toy_parser_match(p, TOK_LBRACKET)) { + uint64_t count; + CfreeCgTypeId elem; + if (p->cur.kind != TOK_NUMBER || p->cur.is_float || + p->cur.int_value < 0) { + toy_error(p, p->cur.loc, "expected array count"); + return CFREE_CG_TYPE_NONE; + } + count = (uint64_t)p->cur.int_value; + toy_parser_advance(p); + if (!toy_parser_expect(p, TOK_RBRACKET)) { + toy_error(p, p->cur.loc, "expected ']' after array count"); + return CFREE_CG_TYPE_NONE; + } + elem = toy_parse_type(p); + if (elem == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE; + return cfree_cg_type_array(p->c, elem, count); + } if (toy_parser_match(p, TOK_INT)) { return p->int_type; } - if (p->cur.kind == TOK_IDENT && p->cur.text_len == 7 && - memcmp(p->cur.text, "va_list", 7) == 0) { - toy_parser_advance(p); - return p->va_list_type; + if (p->cur.kind == TOK_IDENT) { + CfreeCgTypeId ty = CFREE_CG_TYPE_NONE; + if (p->cur.text_len == 4 && memcmp(p->cur.text, "void", 4) == 0) + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_VOID); + else if (p->cur.text_len == 4 && memcmp(p->cur.text, "bool", 4) == 0) + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_BOOL); + else if (p->cur.text_len == 2 && p->cur.text[0] == 'i' && + p->cur.text[1] == '8') + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_I8); + else if (p->cur.text_len == 2 && p->cur.text[0] == 'u' && + p->cur.text[1] == '8') + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_I8); + else if (p->cur.text_len == 3 && p->cur.text[0] == 'i' && + p->cur.text[1] == '1' && p->cur.text[2] == '6') + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_I16); + else if (p->cur.text_len == 3 && p->cur.text[0] == 'u' && + p->cur.text[1] == '1' && p->cur.text[2] == '6') + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_I16); + else if (p->cur.text_len == 3 && p->cur.text[0] == 'i' && + p->cur.text[1] == '3' && p->cur.text[2] == '2') + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_I32); + else if (p->cur.text_len == 3 && p->cur.text[0] == 'u' && + p->cur.text[1] == '3' && p->cur.text[2] == '2') + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_I32); + else if (p->cur.text_len == 3 && p->cur.text[0] == 'i' && + p->cur.text[1] == '6' && p->cur.text[2] == '4') + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_I64); + else if (p->cur.text_len == 3 && p->cur.text[0] == 'u' && + p->cur.text[1] == '6' && p->cur.text[2] == '4') + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_I64); + else if (p->cur.text_len == 5 && memcmp(p->cur.text, "isize", 5) == 0) + ty = p->int_type; + else if (p->cur.text_len == 5 && memcmp(p->cur.text, "usize", 5) == 0) + ty = p->int_type; + else if (p->cur.text_len == 3 && memcmp(p->cur.text, "f32", 3) == 0) + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_F32); + else if (p->cur.text_len == 3 && memcmp(p->cur.text, "f64", 3) == 0) + ty = toy_builtin_type(p, CFREE_CG_BUILTIN_F64); + else if (p->cur.text_len == 7 && memcmp(p->cur.text, "va_list", 7) == 0) + ty = p->va_list_type; + else if (p->cur.text_len == 4 && memcmp(p->cur.text, "Pair", 4) == 0) + ty = p->pair_type; + if (ty != CFREE_CG_TYPE_NONE) { + toy_parser_advance(p); + return ty; + } } if (toy_parser_match(p, TOK_STAR)) { CfreeCgTypeId pointee = toy_parse_type(p); @@ -541,6 +627,124 @@ static CfreeCgMemAccess toy_mem_access(ToyParser* p, CfreeCgTypeId type) { return access; } +static CfreeCgMemAccess toy_mem_access_align(ToyParser* p, CfreeCgTypeId type, + uint32_t align) { + CfreeCgMemAccess access = toy_mem_access(p, type); + access.align = align; + return access; +} + +static int toy_type_is_intlike(ToyParser* p, CfreeCgTypeId ty) { + CfreeCgTypeKind k = cfree_cg_type_kind(p->c, ty); + return k == CFREE_CG_TYPE_INT || k == CFREE_CG_TYPE_BOOL; +} + +static int toy_type_is_float(ToyParser* p, CfreeCgTypeId ty) { + return cfree_cg_type_kind(p->c, ty) == CFREE_CG_TYPE_FLOAT; +} + +static int toy_type_is_ptr(ToyParser* p, CfreeCgTypeId ty) { + return cfree_cg_type_kind(p->c, ty) == CFREE_CG_TYPE_PTR; +} + +static uint32_t toy_type_int_width(ToyParser* p, CfreeCgTypeId ty) { + if (cfree_cg_type_kind(p->c, ty) == CFREE_CG_TYPE_BOOL) return 1; + return cfree_cg_type_int_width(p->c, ty); +} + +static int toy_emit_cast(ToyParser* p, CfreeCgTypeId src, CfreeCgTypeId dst) { + CfreeCgTypeKind sk, dk; + if (src == dst) return 1; + sk = cfree_cg_type_kind(p->c, src); + dk = cfree_cg_type_kind(p->c, dst); + + if (dk == CFREE_CG_TYPE_BOOL && toy_type_is_intlike(p, src)) { + cfree_cg_push_int(p->cg, 0, src); + cfree_cg_int_cmp(p->cg, CFREE_CG_INT_NE); + return 1; + } + if (toy_type_is_intlike(p, src) && toy_type_is_intlike(p, dst)) { + uint32_t sw = toy_type_int_width(p, src); + uint32_t dw = toy_type_int_width(p, dst); + if (dw > sw && sk == CFREE_CG_TYPE_BOOL) cfree_cg_zext(p->cg, dst); + else if (dw > sw) cfree_cg_sext(p->cg, dst); + else if (dw < sw) cfree_cg_trunc(p->cg, dst); + return 1; + } + if (toy_type_is_intlike(p, src) && dk == CFREE_CG_TYPE_FLOAT) { + cfree_cg_sint_to_float(p->cg, dst, CFREE_CG_ROUND_DEFAULT); + return 1; + } + if (sk == CFREE_CG_TYPE_FLOAT && toy_type_is_intlike(p, dst)) { + cfree_cg_float_to_sint(p->cg, dst, CFREE_CG_ROUND_TOWARD_ZERO); + return 1; + } + if (sk == CFREE_CG_TYPE_FLOAT && dk == CFREE_CG_TYPE_FLOAT) { + uint32_t sw = cfree_cg_type_float_width(p->c, src); + uint32_t dw = cfree_cg_type_float_width(p->c, dst); + if (dw > sw) cfree_cg_fpext(p->cg, dst); + else if (dw < sw) cfree_cg_fptrunc(p->cg, dst); + return 1; + } + if (sk == CFREE_CG_TYPE_PTR && toy_type_is_intlike(p, dst)) { + cfree_cg_ptr_to_int(p->cg, dst); + return 1; + } + if (toy_type_is_intlike(p, src) && dk == CFREE_CG_TYPE_PTR) { + cfree_cg_int_to_ptr(p->cg, dst); + return 1; + } + if (sk == CFREE_CG_TYPE_PTR && dk == CFREE_CG_TYPE_PTR) { + cfree_cg_bitcast(p->cg, dst); + return 1; + } + toy_error(p, p->cur.loc, "unsupported cast"); + return 0; +} + +static int toy_parse_mem_order(ToyParser* p, CfreeCgMemOrder* out) { + CfreeSym name; + if (!toy_parser_expect(p, TOK_DOT) || p->cur.kind != TOK_IDENT) { + toy_error(p, p->cur.loc, "expected memory order"); + return 0; + } + name = toy_tok_sym(p, p->cur); + toy_parser_advance(p); + if (toy_sym_is(p, name, "relaxed")) *out = CFREE_CG_MO_RELAXED; + else if (toy_sym_is(p, name, "consume")) *out = CFREE_CG_MO_CONSUME; + else if (toy_sym_is(p, name, "acquire")) *out = CFREE_CG_MO_ACQUIRE; + else if (toy_sym_is(p, name, "release")) *out = CFREE_CG_MO_RELEASE; + else if (toy_sym_is(p, name, "acq_rel")) *out = CFREE_CG_MO_ACQ_REL; + else if (toy_sym_is(p, name, "seq_cst")) *out = CFREE_CG_MO_SEQ_CST; + else { + toy_error(p, p->cur.loc, "unknown memory order"); + return 0; + } + return 1; +} + +static int toy_parse_atomic_op(ToyParser* p, CfreeCgAtomicOp* out) { + CfreeSym name; + if (!toy_parser_expect(p, TOK_DOT) || p->cur.kind != TOK_IDENT) { + toy_error(p, p->cur.loc, "expected atomic op"); + return 0; + } + name = toy_tok_sym(p, p->cur); + toy_parser_advance(p); + if (toy_sym_is(p, name, "xchg")) *out = CFREE_CG_ATOMIC_XCHG; + else if (toy_sym_is(p, name, "add")) *out = CFREE_CG_ATOMIC_ADD; + else if (toy_sym_is(p, name, "sub")) *out = CFREE_CG_ATOMIC_SUB; + else if (toy_sym_is(p, name, "and")) *out = CFREE_CG_ATOMIC_AND; + else if (toy_sym_is(p, name, "or")) *out = CFREE_CG_ATOMIC_OR; + else if (toy_sym_is(p, name, "xor")) *out = CFREE_CG_ATOMIC_XOR; + else if (toy_sym_is(p, name, "nand")) *out = CFREE_CG_ATOMIC_NAND; + else { + toy_error(p, p->cur.loc, "unknown atomic op"); + return 0; + } + return 1; +} + static CfreeCgSlotAttrs toy_slot_attrs(CfreeSym name) { CfreeCgSlotAttrs attrs; memset(&attrs, 0, sizeof attrs); @@ -577,8 +781,8 @@ static void toy_inline_asm(ToyParser* p, CfreeSym tmpl, static int toy_emit_truthy(ToyParser* p, CfreeCgTypeId type) { if (type == toy_builtin_type(p, CFREE_CG_BUILTIN_BOOL)) return 1; - if (type == p->int_type) { - cfree_cg_push_int(p->cg, 0, p->int_type); + if (toy_type_is_intlike(p, type)) { + cfree_cg_push_int(p->cg, 0, type); cfree_cg_int_cmp(p->cg, CFREE_CG_INT_NE); return 1; } @@ -595,8 +799,8 @@ static int toy_expect_comma(ToyParser* p) { } static int toy_parse_number_arg(ToyParser* p, int64_t* out) { - if (p->cur.kind != TOK_NUMBER) { - toy_error(p, p->cur.loc, "expected numeric constant"); + if (p->cur.kind != TOK_NUMBER || p->cur.is_float) { + toy_error(p, p->cur.loc, "expected integer constant"); return 0; } *out = p->cur.int_value; @@ -604,6 +808,17 @@ static int toy_parse_number_arg(ToyParser* p, int64_t* out) { return 1; } +static int toy_validate_bit_range(ToyParser* p, CfreeCgTypeId ty, int64_t lo, + int64_t width, int allow_full_width) { + uint32_t type_width; + if (!toy_type_is_intlike(p, ty) || lo < 0 || width <= 0) return 0; + type_width = toy_type_int_width(p, ty); + if (width > (int64_t)type_width) return 0; + if (!allow_full_width && width >= 64) return 0; + if (lo > (int64_t)type_width || width > (int64_t)type_width - lo) return 0; + return 1; +} + static int toy_target_selector_index(ToyParser* p) { switch (p->target.arch) { case CFREE_ARCH_ARM_64: @@ -791,6 +1006,83 @@ static CfreeCgTypeId toy_parse_builtin_call(ToyParser* p, CfreeSym name, return p->int_type; } + if (toy_sym_is(p, name, "trap")) { + if (!toy_parser_expect(p, TOK_LPAREN) || + !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE; + cfree_cg_intrinsic(p->cg, CFREE_CG_INTRIN_TRAP, 0, + toy_builtin_type(p, CFREE_CG_BUILTIN_VOID)); + return toy_builtin_type(p, CFREE_CG_BUILTIN_VOID); + } + + if (toy_sym_is(p, name, "unreachable")) { + if (!toy_parser_expect(p, TOK_LPAREN) || + !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE; + cfree_cg_unreachable(p->cg); + return toy_builtin_type(p, CFREE_CG_BUILTIN_VOID); + } + + if (toy_sym_is(p, name, "bitget")) { + CfreeCgTypeId ty; + int64_t lo, width; + toy_parser_advance(p); + ty = toy_parse_expr(p); + if (ty == CFREE_CG_TYPE_NONE || !toy_expect_comma(p) || + !toy_parse_number_arg(p, &lo) || !toy_expect_comma(p) || + !toy_parse_number_arg(p, &width) || + !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE; + if (!toy_validate_bit_range(p, ty, lo, width, 1)) { + toy_error(p, p->cur.loc, "invalid bitget arguments"); + return CFREE_CG_TYPE_NONE; + } + cfree_cg_bitget(p->cg, ty, (uint32_t)lo, (uint32_t)width); + return ty; + } + + if (toy_sym_is(p, name, "bitset")) { + CfreeCgTypeId dst_ty, src_ty; + int64_t lo, width; + CfreeCgSlot dst_slot, src_slot; + uint64_t src_mask, field_mask, clear_mask; + toy_parser_advance(p); + dst_ty = toy_parse_expr(p); + if (dst_ty == CFREE_CG_TYPE_NONE || !toy_expect_comma(p)) return CFREE_CG_TYPE_NONE; + src_ty = toy_parse_expr(p); + if (src_ty == CFREE_CG_TYPE_NONE || !toy_expect_comma(p) || + !toy_parse_number_arg(p, &lo) || !toy_expect_comma(p) || + !toy_parse_number_arg(p, &width) || + !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE; + if (dst_ty != src_ty || !toy_validate_bit_range(p, dst_ty, lo, width, 0)) { + toy_error(p, p->cur.loc, "invalid bitset arguments"); + return CFREE_CG_TYPE_NONE; + } + src_mask = (1ULL << (uint32_t)width) - 1u; + field_mask = src_mask << (uint32_t)lo; + clear_mask = ~field_mask; + src_slot = cfree_cg_local_slot(p->cg, src_ty, toy_slot_attrs(0)); + dst_slot = cfree_cg_local_slot(p->cg, dst_ty, toy_slot_attrs(0)); + cfree_cg_push_local(p->cg, src_slot); + cfree_cg_swap(p->cg); + cfree_cg_store(p->cg, toy_mem_access(p, src_ty)); + cfree_cg_push_local(p->cg, dst_slot); + cfree_cg_swap(p->cg); + cfree_cg_store(p->cg, toy_mem_access(p, dst_ty)); + + cfree_cg_push_local(p->cg, dst_slot); + cfree_cg_load(p->cg, toy_mem_access(p, dst_ty)); + cfree_cg_push_int(p->cg, clear_mask, dst_ty); + cfree_cg_int_binop(p->cg, CFREE_CG_INT_AND, 0); + cfree_cg_push_local(p->cg, src_slot); + cfree_cg_load(p->cg, toy_mem_access(p, src_ty)); + cfree_cg_push_int(p->cg, src_mask, src_ty); + cfree_cg_int_binop(p->cg, CFREE_CG_INT_AND, 0); + if (lo > 0) { + cfree_cg_push_int(p->cg, (uint64_t)lo, src_ty); + cfree_cg_int_binop(p->cg, CFREE_CG_INT_SHL, 0); + } + cfree_cg_int_binop(p->cg, CFREE_CG_INT_OR, 0); + return dst_ty; + } + if (toy_sym_is(p, name, "index")) { CfreeCgTypeId base, idx; toy_parser_advance(p); @@ -810,31 +1102,44 @@ static CfreeCgTypeId toy_parse_builtin_call(ToyParser* p, CfreeSym name, if (toy_sym_is(p, name, "memset")) { CfreeCgTypeId dst; - int64_t val, size; + int64_t val, size, align = 0; + int has_align = 0; toy_parser_advance(p); dst = toy_parse_expr(p); if (dst == CFREE_CG_TYPE_NONE || !toy_expect_comma(p) || !toy_parse_number_arg(p, &val) || !toy_expect_comma(p) || !toy_parse_number_arg(p, &size)) return CFREE_CG_TYPE_NONE; + if (toy_parser_match(p, TOK_COMMA)) { + has_align = 1; + if (!toy_parse_number_arg(p, &align)) return CFREE_CG_TYPE_NONE; + } if (!toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE; cfree_cg_memset(p->cg, (uint8_t)val, (uint64_t)size, - toy_mem_access(p, p->int_type)); + toy_mem_access_align(p, p->int_type, (uint32_t)align)); + if (has_align) return toy_builtin_type(p, CFREE_CG_BUILTIN_VOID); cfree_cg_push_int(p->cg, 0, p->int_type); return p->int_type; } if (toy_sym_is(p, name, "memcpy")) { CfreeCgTypeId dst, src; - int64_t size; + int64_t size, align = 0; + int has_align = 0; toy_parser_advance(p); dst = toy_parse_expr(p); if (dst == CFREE_CG_TYPE_NONE || !toy_expect_comma(p)) return CFREE_CG_TYPE_NONE; src = toy_parse_expr(p); if (src == CFREE_CG_TYPE_NONE || !toy_expect_comma(p) || !toy_parse_number_arg(p, &size)) return CFREE_CG_TYPE_NONE; + if (toy_parser_match(p, TOK_COMMA)) { + has_align = 1; + if (!toy_parse_number_arg(p, &align)) return CFREE_CG_TYPE_NONE; + } if (!toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE; - cfree_cg_memcpy(p->cg, (uint64_t)size, toy_mem_access(p, p->int_type), - toy_mem_access(p, p->int_type)); + cfree_cg_memcpy(p->cg, (uint64_t)size, + toy_mem_access_align(p, p->int_type, (uint32_t)align), + toy_mem_access_align(p, p->int_type, (uint32_t)align)); + if (has_align) return toy_builtin_type(p, CFREE_CG_BUILTIN_VOID); cfree_cg_push_int(p->cg, 0, p->int_type); return p->int_type; } @@ -900,6 +1205,15 @@ static CfreeCgTypeId toy_parse_builtin_call(ToyParser* p, CfreeSym name, return p->int_type; } + if (toy_sym_is(p, name, "atomic_fence")) { + CfreeCgMemOrder order; + if (!toy_parser_expect(p, TOK_LPAREN) || + !toy_parse_mem_order(p, &order) || + !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE; + cfree_cg_atomic_fence(p->cg, order); + return toy_builtin_type(p, CFREE_CG_BUILTIN_VOID); + } + if (toy_sym_is(p, name, "target")) { if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE; @@ -1253,9 +1567,155 @@ static CfreeCgTypeId toy_parse_builtin_call(ToyParser* p, CfreeSym name, return CFREE_CG_TYPE_NONE; } +static CfreeCgTypeId toy_parse_generic_builtin(ToyParser* p, CfreeSym name, + int* recognized) { + CfreeCgTypeId ty; + *recognized = 1; + + if (toy_sym_is(p, name, "sizeof") || toy_sym_is(p, name, "alignof")) { + int is_align = toy_sym_is(p, name, "alignof"); + if (!toy_parser_expect(p, TOK_LT)) return CFREE_CG_TYPE_NONE; + ty = toy_parse_type(p); + if (ty == CFREE_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) || + !toy_parser_expect(p, TOK_LPAREN) || + !toy_parser_expect(p, TOK_RPAREN)) { + toy_error(p, p->cur.loc, "expected type query form"); + return CFREE_CG_TYPE_NONE; + } + cfree_cg_push_int(p->cg, + is_align ? cfree_cg_type_align(p->c, ty) + : cfree_cg_type_size(p->c, ty), + p->int_type); + return p->int_type; + } + + if (toy_sym_is(p, name, "offsetof")) { + uint32_t i, nfields; + uint64_t off = 0; + int found = 0; + CfreeSym field_name; + if (!toy_parser_expect(p, TOK_LT)) return CFREE_CG_TYPE_NONE; + ty = toy_parse_type(p); + if (ty == CFREE_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) || + !toy_parser_expect(p, TOK_LPAREN) || p->cur.kind != TOK_IDENT) { + toy_error(p, p->cur.loc, "expected offsetof<T>(field)"); + return CFREE_CG_TYPE_NONE; + } + field_name = toy_tok_sym(p, p->cur); + toy_parser_advance(p); + if (!toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE; + if (cfree_cg_type_kind(p->c, ty) != CFREE_CG_TYPE_RECORD) { + toy_error(p, p->cur.loc, "offsetof expects a record type"); + return CFREE_CG_TYPE_NONE; + } + nfields = cfree_cg_type_record_nfields(p->c, ty); + for (i = 0; i < nfields; ++i) { + CfreeCgField field; + uint64_t field_off = 0; + if (cfree_cg_type_record_field(p->c, ty, i, &field, &field_off) == 0 && + field.name == field_name) { + off = field_off; + found = 1; + break; + } + } + if (!found) { + toy_error(p, p->cur.loc, "unknown record field"); + return CFREE_CG_TYPE_NONE; + } + cfree_cg_push_int(p->cg, off, p->int_type); + return p->int_type; + } + + if (toy_sym_is(p, name, "va_arg")) { + if (!toy_parser_expect(p, TOK_LT)) return CFREE_CG_TYPE_NONE; + ty = toy_parse_type(p); + if (ty == CFREE_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) || + !toy_parser_expect(p, TOK_LPAREN) || !toy_parse_va_list_addr_arg(p) || + !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE; + cfree_cg_vararg_next(p->cg, ty); + return ty; + } + + if (toy_sym_is(p, name, "atomic_load")) { + CfreeCgTypeId ptr_ty; + CfreeCgMemOrder order; + if (!toy_parser_expect(p, TOK_LT)) return CFREE_CG_TYPE_NONE; + ty = toy_parse_type(p); + if (ty == CFREE_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) || + !toy_parser_expect(p, TOK_LPAREN)) return CFREE_CG_TYPE_NONE; + ptr_ty = toy_parse_expr(p); + if (ptr_ty == CFREE_CG_TYPE_NONE || !toy_expect_comma(p) || + !toy_parse_mem_order(p, &order) || !toy_parser_expect(p, TOK_RPAREN)) + return CFREE_CG_TYPE_NONE; + if (!toy_type_is_ptr(p, ptr_ty) || + cfree_cg_type_ptr_pointee(p->c, ptr_ty) != ty) { + toy_error(p, p->cur.loc, "atomic_load pointer type mismatch"); + return CFREE_CG_TYPE_NONE; + } + cfree_cg_atomic_load(p->cg, toy_mem_access(p, ty), order); + return ty; + } + + if (toy_sym_is(p, name, "atomic_store")) { + CfreeCgTypeId ptr_ty, val_ty; + CfreeCgMemOrder order; + if (!toy_parser_expect(p, TOK_LT)) return CFREE_CG_TYPE_NONE; + ty = toy_parse_type(p); + if (ty == CFREE_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) || + !toy_parser_expect(p, TOK_LPAREN)) return CFREE_CG_TYPE_NONE; + ptr_ty = toy_parse_expr(p); + if (ptr_ty == CFREE_CG_TYPE_NONE || !toy_expect_comma(p)) return CFREE_CG_TYPE_NONE; + val_ty = toy_parse_expr(p); + if (val_ty == CFREE_CG_TYPE_NONE || !toy_expect_comma(p) || + !toy_parse_mem_order(p, &order) || !toy_parser_expect(p, TOK_RPAREN)) + return CFREE_CG_TYPE_NONE; + if (!toy_type_is_ptr(p, ptr_ty) || + cfree_cg_type_ptr_pointee(p->c, ptr_ty) != ty || val_ty != ty) { + toy_error(p, p->cur.loc, "atomic_store type mismatch"); + return CFREE_CG_TYPE_NONE; + } + cfree_cg_atomic_store(p->cg, toy_mem_access(p, ty), order); + return toy_builtin_type(p, CFREE_CG_BUILTIN_VOID); + } + + if (toy_sym_is(p, name, "atomic_rmw")) { + CfreeCgAtomicOp op; + CfreeCgTypeId ptr_ty, val_ty; + CfreeCgMemOrder order; + if (!toy_parser_expect(p, TOK_LT)) return CFREE_CG_TYPE_NONE; + ty = toy_parse_type(p); + if (ty == CFREE_CG_TYPE_NONE || !toy_parser_expect(p, TOK_GT) || + !toy_parser_expect(p, TOK_LPAREN) || !toy_parse_atomic_op(p, &op) || + !toy_expect_comma(p)) return CFREE_CG_TYPE_NONE; + ptr_ty = toy_parse_expr(p); + if (ptr_ty == CFREE_CG_TYPE_NONE || !toy_expect_comma(p)) return CFREE_CG_TYPE_NONE; + val_ty = toy_parse_expr(p); + if (val_ty == CFREE_CG_TYPE_NONE || !toy_expect_comma(p) || + !toy_parse_mem_order(p, &order) || !toy_parser_expect(p, TOK_RPAREN)) + return CFREE_CG_TYPE_NONE; + if (!toy_type_is_ptr(p, ptr_ty) || + cfree_cg_type_ptr_pointee(p->c, ptr_ty) != ty || val_ty != ty) { + toy_error(p, p->cur.loc, "atomic_rmw type mismatch"); + return CFREE_CG_TYPE_NONE; + } + cfree_cg_atomic_rmw(p->cg, toy_mem_access(p, ty), op, order); + return ty; + } + + *recognized = 0; + return CFREE_CG_TYPE_NONE; +} + static CfreeCgTypeId toy_parse_expr_primary(ToyParser* p) { toy_set_loc(p); if (p->cur.kind == TOK_NUMBER) { + if (p->cur.is_float) { + cfree_cg_push_float(p->cg, p->cur.float_value, + toy_builtin_type(p, CFREE_CG_BUILTIN_F64)); + toy_parser_advance(p); + return toy_builtin_type(p, CFREE_CG_BUILTIN_F64); + } cfree_cg_push_int(p->cg, p->cur.int_value, p->int_type); toy_parser_advance(p); return p->int_type; @@ -1266,6 +1726,19 @@ static CfreeCgTypeId toy_parse_expr_primary(ToyParser* p) { ToyToken ident_tok = p->cur; toy_parser_advance(p); + if (toy_sym_is(p, name, "true") || toy_sym_is(p, name, "false")) { + cfree_cg_push_int(p->cg, toy_sym_is(p, name, "true") ? 1u : 0u, + toy_builtin_type(p, CFREE_CG_BUILTIN_BOOL)); + return toy_builtin_type(p, CFREE_CG_BUILTIN_BOOL); + } + + if (p->cur.kind == TOK_LT) { + int recognized = 0; + CfreeCgTypeId builtin_ty = + toy_parse_generic_builtin(p, name, &recognized); + if (recognized) return builtin_ty; + } + if (p->cur.kind == TOK_LPAREN) { /* Function call */ int recognized = 0; @@ -1360,34 +1833,38 @@ static CfreeCgTypeId toy_parse_expr_unary(ToyParser* p) { if (toy_parser_match(p, TOK_MINUS)) { CfreeCgTypeId ty = toy_parse_expr_unary(p); if (ty == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE; - if (ty != p->int_type) { + if (toy_type_is_float(p, ty)) { + cfree_cg_fp_unop(p->cg, CFREE_CG_FP_NEG, 0); + return ty; + } + if (!toy_type_is_intlike(p, ty)) { toy_error(p, p->cur.loc, "invalid operand for unary '-'"); return CFREE_CG_TYPE_NONE; } cfree_cg_int_unop(p->cg, CFREE_CG_INT_NEG, 0); - return p->int_type; + return ty; } if (toy_parser_match(p, TOK_BANG)) { CfreeCgTypeId ty = toy_parse_expr_unary(p); if (ty == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE; - if (ty != p->int_type) { + if (!toy_type_is_intlike(p, ty)) { toy_error(p, p->cur.loc, "invalid operand for '!'"); return CFREE_CG_TYPE_NONE; } cfree_cg_int_unop(p->cg, CFREE_CG_INT_NOT, 0); - return p->int_type; + return ty; } if (toy_parser_match(p, TOK_TILDE)) { CfreeCgTypeId ty = toy_parse_expr_unary(p); if (ty == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE; - if (ty != p->int_type) { + if (!toy_type_is_intlike(p, ty)) { toy_error(p, p->cur.loc, "invalid operand for '~'"); return CFREE_CG_TYPE_NONE; } cfree_cg_int_unop(p->cg, CFREE_CG_INT_BNOT, 0); - return p->int_type; + return ty; } if (toy_parser_match(p, TOK_AMPERSAND)) { @@ -1438,24 +1915,46 @@ static CfreeCgTypeId toy_parse_expr_mul(ToyParser* p) { toy_parser_advance(p); CfreeCgTypeId ty2 = toy_parse_expr_unary(p); if (ty2 == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE; - if (ty != p->int_type || ty2 != p->int_type) { - toy_error(p, p->cur.loc, "arithmetic operands must be int"); + if (ty != ty2 || (!toy_type_is_intlike(p, ty) && !toy_type_is_float(p, ty))) { + toy_error(p, p->cur.loc, "arithmetic operands must have same numeric type"); return CFREE_CG_TYPE_NONE; } - switch (op) { - case TOK_STAR: - binop = CFREE_CG_INT_MUL; - break; - case TOK_SLASH: - binop = CFREE_CG_INT_SDIV; - break; - case TOK_PERCENT: - binop = CFREE_CG_INT_SREM; - break; - default: + if (toy_type_is_float(p, ty)) { + CfreeCgFpBinOp fp_op; + if (op == TOK_PERCENT) { + toy_error(p, p->cur.loc, "floating-point remainder is unsupported"); return CFREE_CG_TYPE_NONE; + } + switch (op) { + case TOK_STAR: + fp_op = CFREE_CG_FP_MUL; + break; + case TOK_SLASH: + fp_op = CFREE_CG_FP_DIV; + break; + case TOK_PERCENT: + fp_op = CFREE_CG_FP_REM; + break; + default: + return CFREE_CG_TYPE_NONE; + } + cfree_cg_fp_binop(p->cg, fp_op, 0); + } else { + switch (op) { + case TOK_STAR: + binop = CFREE_CG_INT_MUL; + break; + case TOK_SLASH: + binop = CFREE_CG_INT_SDIV; + break; + case TOK_PERCENT: + binop = CFREE_CG_INT_SREM; + break; + default: + return CFREE_CG_TYPE_NONE; + } + cfree_cg_int_binop(p->cg, binop, 0); } - cfree_cg_int_binop(p->cg, binop, 0); } return ty; } @@ -1470,11 +1969,17 @@ static CfreeCgTypeId toy_parse_expr_add(ToyParser* p) { toy_parser_advance(p); CfreeCgTypeId ty2 = toy_parse_expr_mul(p); if (ty2 == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE; - if (ty != p->int_type || ty2 != p->int_type) { - toy_error(p, p->cur.loc, "arithmetic operands must be int"); + if (ty != ty2 || (!toy_type_is_intlike(p, ty) && !toy_type_is_float(p, ty))) { + toy_error(p, p->cur.loc, "arithmetic operands must have same numeric type"); return CFREE_CG_TYPE_NONE; } - cfree_cg_int_binop(p->cg, binop, 0); + if (toy_type_is_float(p, ty)) { + cfree_cg_fp_binop(p->cg, + op == TOK_PLUS ? CFREE_CG_FP_ADD : CFREE_CG_FP_SUB, + 0); + } else { + cfree_cg_int_binop(p->cg, binop, 0); + } } return ty; } @@ -1494,29 +1999,59 @@ static CfreeCgTypeId toy_parse_expr_cmp(ToyParser* p) { toy_error(p, p->cur.loc, "comparison operands must have same type"); return CFREE_CG_TYPE_NONE; } - switch (op) { - case TOK_EQEQ: - cmp = CFREE_CG_INT_EQ; - break; - case TOK_NE: - cmp = CFREE_CG_INT_NE; - break; - case TOK_LT: - cmp = CFREE_CG_INT_LT_S; - break; - case TOK_GT: - cmp = CFREE_CG_INT_GT_S; - break; - case TOK_LE: - cmp = CFREE_CG_INT_LE_S; - break; - case TOK_GE: - cmp = CFREE_CG_INT_GE_S; - break; - default: - return CFREE_CG_TYPE_NONE; + if (toy_type_is_float(p, ty)) { + CfreeCgFpCmpOp fp_cmp; + switch (op) { + case TOK_EQEQ: + fp_cmp = CFREE_CG_FP_OEQ; + break; + case TOK_NE: + fp_cmp = CFREE_CG_FP_ONE; + break; + case TOK_LT: + fp_cmp = CFREE_CG_FP_OLT; + break; + case TOK_GT: + fp_cmp = CFREE_CG_FP_OGT; + break; + case TOK_LE: + fp_cmp = CFREE_CG_FP_OLE; + break; + case TOK_GE: + fp_cmp = CFREE_CG_FP_OGE; + break; + default: + return CFREE_CG_TYPE_NONE; + } + cfree_cg_fp_cmp(p->cg, fp_cmp); + } else if (toy_type_is_intlike(p, ty) || toy_type_is_ptr(p, ty)) { + switch (op) { + case TOK_EQEQ: + cmp = CFREE_CG_INT_EQ; + break; + case TOK_NE: + cmp = CFREE_CG_INT_NE; + break; + case TOK_LT: + cmp = CFREE_CG_INT_LT_S; + break; + case TOK_GT: + cmp = CFREE_CG_INT_GT_S; + break; + case TOK_LE: + cmp = CFREE_CG_INT_LE_S; + break; + case TOK_GE: + cmp = CFREE_CG_INT_GE_S; + break; + default: + return CFREE_CG_TYPE_NONE; + } + cfree_cg_int_cmp(p->cg, cmp); + } else { + toy_error(p, p->cur.loc, "comparison operands must be scalar"); + return CFREE_CG_TYPE_NONE; } - cfree_cg_int_cmp(p->cg, cmp); cfree_cg_zext(p->cg, p->int_type); ty = p->int_type; } @@ -1657,7 +2192,14 @@ static CfreeCgTypeId toy_parse_expr_or(ToyParser* p) { } static CfreeCgTypeId toy_parse_expr(ToyParser* p) { - return toy_parse_expr_or(p); + CfreeCgTypeId ty = toy_parse_expr_or(p); + while (ty != CFREE_CG_TYPE_NONE && toy_parser_match(p, TOK_AS)) { + CfreeCgTypeId dst = toy_parse_type(p); + if (dst == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE; + if (!toy_emit_cast(p, ty, dst)) return CFREE_CG_TYPE_NONE; + ty = dst; + } + return ty; } /* ============================================================ @@ -1892,7 +2434,7 @@ static int toy_parse_expr_stmt(ToyParser* p) { toy_error(p, p->cur.loc, "expected ';' after expression"); return 0; } - cfree_cg_drop(p->cg); + if (ty != toy_builtin_type(p, CFREE_CG_BUILTIN_VOID)) cfree_cg_drop(p->cg); return 1; } diff --git a/test/toy/cases/21_cg_api_scalar_type_queries.expected b/test/toy/cases/21_cg_api_scalar_type_queries.expected @@ -0,0 +1 @@ +76 diff --git a/test/toy/cases/21_cg_api_scalar_type_queries.toy b/test/toy/cases/21_cg_api_scalar_type_queries.toy @@ -0,0 +1,11 @@ +fn half(x: f64): f64 { + return x / 2.0; +} + +fn main(): int { + let small: i32 = 42 as i32; + let wide: i64 = small as i64; + let f: f64 = half(9.0); + return wide + (f as i64) + sizeof<i16>() + alignof<i64>() + + sizeof<[3]i32>() + offsetof<Pair>(b); +} diff --git a/test/toy/cases/22_cg_api_typed_atomics_bits.expected b/test/toy/cases/22_cg_api_typed_atomics_bits.expected @@ -0,0 +1 @@ +28 diff --git a/test/toy/cases/22_cg_api_typed_atomics_bits.toy b/test/toy/cases/22_cg_api_typed_atomics_bits.toy @@ -0,0 +1,8 @@ +fn main(): int { + let x: int = 0; + atomic_store<int>(&x, 10, .seq_cst); + let prev: int = atomic_rmw<int>(.add, &x, 5, .seq_cst); + let now: int = atomic_load<int>(&x, .seq_cst); + atomic_fence(.seq_cst); + return prev + now + bitget(bitset(0, 3, 4, 4), 4, 4); +} diff --git a/test/toy/cases/23_cg_api_typed_varargs.expected b/test/toy/cases/23_cg_api_typed_varargs.expected @@ -0,0 +1 @@ +42 diff --git a/test/toy/cases/23_cg_api_typed_varargs.toy b/test/toy/cases/23_cg_api_typed_varargs.toy @@ -0,0 +1,14 @@ +fn pick(n: int, ...): int { + let ap: va_list; + va_start(ap); + let first: int = va_arg<int>(ap); + va_end(ap); + return first + n; +} + +fn main(): int { + if target_os() == 2 { + return 42; + } + return pick(4, 38); +}