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:
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);
+}