diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2023-10-28 00:00:21 +0300 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2023-10-28 00:00:21 +0300 |
| commit | d96b8c7d12fc61ec609d6138627b9d6d18139a9a (patch) | |
| tree | 82bc9a4c2b22de6c021097fb79a57b6d7756a127 /tasm/src | |
| download | tri-d96b8c7d12fc61ec609d6138627b9d6d18139a9a.tar.gz tri-d96b8c7d12fc61ec609d6138627b9d6d18139a9a.zip | |
move to monorepo
+ Will probably have to make some changes to dir layout to make things
make more sense
Diffstat (limited to 'tasm/src')
| -rw-r--r-- | tasm/src/assembler.c | 393 | ||||
| -rw-r--r-- | tasm/src/bits.c | 75 | ||||
| -rw-r--r-- | tasm/src/lexer.l | 237 | ||||
| -rw-r--r-- | tasm/src/main.c | 68 | ||||
| -rw-r--r-- | tasm/src/parser.y | 532 | ||||
| -rw-r--r-- | tasm/src/source.mk | 8 |
6 files changed, 1313 insertions, 0 deletions
diff --git a/tasm/src/assembler.c b/tasm/src/assembler.c new file mode 100644 index 0000000..c0c05be --- /dev/null +++ b/tasm/src/assembler.c @@ -0,0 +1,393 @@ +#include <tasm/assembler.h> +#include <tasm/parser.h> +#include <tasm/bits.h> + +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <limits.h> +#include <stdio.h> +#include <errno.h> + +struct asm_ctx { + size_t size; + size_t idx; + tri_t *buf; +}; + +static void emit(struct asm_ctx *ctx, tri_t i) +{ + if (ctx->size == ctx->idx) { + ctx->size *= 2; + ctx->buf = realloc(ctx->buf, ctx->size * sizeof(tri_t)); + } + + ctx->buf[ctx->idx++] = i; +} + +static bool check_imm(tri_t imm, size_t n) +{ + /* check that we can fit imm into n trits */ + return tri_mask(imm, n) == imm; +} + +tri_t parse_imm(const char *imm) +{ + size_t len = strlen(imm); + if (len < 2) { + /* must be base 10 */ + char *end = NULL; + int64_t val = strtoll(imm, &end, 10); + assert(imm + len == end); + return tri_from(val); + } + + if (strncmp(imm, "0t", 2) == 0) { + /* must be raw trinary */ + return tri_parse_default(imm, len); + } + + if (strncmp(imm, "0h", 2) == 0) { + /* must be heptavintimal */ + fprintf(stderr, "heptavintimal not yet implemented\n"); + abort(); + return 0; + } + + /* try to let strtoll figure out */ + char *end = NULL; + int64_t val = strtoll(imm, &end, 0); + if (imm + len == end) + return tri_from(val); + + + fprintf(stderr, "unrecognised immediate: %s\n", imm); + abort(); + return 0; +} + +void emit_r(struct asm_ctx *ctx, tri_t opcode, + enum gpr_num rd, tri_t fn0, enum gpr_num rs1, + enum gpr_num rs2, tri_t fn5) +{ + tri_t xrd = gpr_tri(rd ); + tri_t xrs1 = gpr_tri(rs1); + tri_t xrs2 = gpr_tri(rs2); + tri_t i = build_r(opcode, xrd, fn0, xrs1, xrs2, fn5); + emit(ctx, i); +} + +void emit_i(struct asm_ctx *ctx, tri_t opcode, + enum gpr_num rd, tri_t fn0, enum gpr_num rs1, tri_t imm9) +{ + tri_t xrd = gpr_tri(rd ); + tri_t xrs1 = gpr_tri(rs1); + if (!check_imm(imm9, 9)) { + fprintf(stderr, "immediate %lx doesn't fit into 9 trits\n", imm9); + abort(); + return; + } + tri_t i = build_i(opcode, xrd, fn0, xrs1, imm9); + emit(ctx, i); +} + +void emit_s(struct asm_ctx *ctx, tri_t opcode, + tri_t fn0, enum gpr_num rs1, enum gpr_num rs2, tri_t imm9) +{ + tri_t xrs1 = gpr_tri(rs1); + tri_t xrs2 = gpr_tri(rs2); + if (!check_imm(imm9, 9)) { + fprintf(stderr, "immediate %lx doesn't fit into 9 trits\n", imm9); + abort(); + return; + } + tri_t imm4 = tri_mask(imm9, 4); + tri_t imm5 = tri_mask(tri_sr(imm9, 4), 5); + + tri_t i = build_s(opcode, imm4, fn0, xrs1, xrs2, imm5); + emit(ctx, i); +} + +void emit_u(struct asm_ctx *ctx, tri_t opcode, + enum gpr_num rd, tri_t imm18) +{ + tri_t xrd = gpr_tri(rd); + if (!check_imm(imm18, 18)) { + fprintf(stderr, "immediate %lx does not fit into 18 trits\n", imm18); + abort(); + return; + } + + tri_t i = build_u(opcode, xrd, imm18); + emit(ctx, i); +} + +void emit_d(struct asm_ctx *ctx, tri_t opcode, + enum gpr_num rd, enum gpr_num rs1, enum gpr_num rs2, tri_t imm10) +{ + tri_t xrd = gpr_tri(rd ); + tri_t xrs1 = gpr_tri(rs1); + tri_t xrs2 = gpr_tri(rs2); + + if (!check_imm(imm10, 10)) { + fprintf(stderr, "immediate %lx does not fit into 10 trits\n", imm10); + abort(); + return; + } + + tri_t imm0 = tri_mask(imm10, 5); + tri_t imm1 = tri_mask(tri_sr(imm10, 5), 5); + + tri_t i = build_r(opcode, xrd, imm0, xrs1, xrs2, imm1); + emit(ctx, i); +} + +static char *tasm_basename(const char *file) +{ + size_t l = strlen(file); + size_t n = l - 1; + while (--n) { + if (file[n] == '/') + break; + } + + if (n == 0) + return strdup(file); + + return strndup(file + n + 1, l - n); +} + +static char *tasm_dirname(const char *file) +{ + size_t l = strlen(file); + size_t n = l - 1; + while (--n) { + if (file[n] == '/') + break; + } + + return strndup(file, n); +} + +static char *tasm_cwdname() +{ + size_t size; + long path_max = pathconf(".", _PC_PATH_MAX); + if (path_max == -1) + size = 1024; + else + size = (size_t)path_max; + + char *buf = malloc(size); + if (!buf) + return NULL; + + if (!getcwd(buf, size)) { + fprintf(stderr, "%s\n", strerror(errno)); + free(buf); + return NULL; + } + + return buf; +} + +static char *read_file(const char *file, FILE *f) +{ + fseek(f, 0, SEEK_END); + /** @todo check how well standardized this actually is */ + long s = ftell(f); + if (s == LONG_MAX) { + fprintf(stderr, "%s might be a directory", file); + return NULL; + } + + fseek(f, 0, SEEK_SET); + + char *buf = malloc(s + 1); + if (!buf) + return NULL; + + fread(buf, s + 1, 1, f); + /* remember terminating null */ + buf[s] = 0; + return buf; +} + +static int process(struct asm_ctx *ctx, const char *file) +{ + FILE *f = fopen(file, "rb"); + if (!f) { + fprintf(stderr, "failed opening %s: %s\n", file, strerror(errno)); + return -1; + } + + const char *buf = read_file(file, f); + fclose(f); + + if (!buf) + return -1; + + struct parser *p = create_parser(); + parse(p, ctx, file, buf); + bool failed = p->failed; + destroy_parser(p); + + free((void *)buf); + + if (failed) + return -1; + + return 0; +} + +/** @todo eventually take context */ +int process_file(struct asm_ctx *ctx, const char *file) +{ + const char *base = tasm_basename(file); + const char *dir = tasm_dirname(file); + + const char *cwd = tasm_cwdname(); + + chdir(dir); + int res = process(ctx, base); + chdir(cwd); + + free((void *)base); + free((void *)dir); + free((void *)cwd); + + return res; +} + +int assemble(const char *outfile, const char *infile) +{ + /** @todo cleanup */ + struct asm_ctx ctx = {.size = 1, .buf = malloc(sizeof(tri_t)), .idx = 0}; + int ret = process_file(&ctx, infile); + if (ret) + return ret; + + FILE *f = fopen(outfile, "wb"); + if (!f) + return -1; + + for (size_t i = 0; i < ctx.idx; ++i) { + tri_t t = ctx.buf[i]; + /* output trytewise LE */ + uint32_t t0 = tri_mask(t, 9); + uint32_t t1 = tri_mask(tri_sr(t, 9), 9); + uint32_t t2 = tri_mask(tri_sr(t, 18), 9); + + size_t c0 = fwrite(&t0, sizeof(t0), 1, f); + size_t c1 = fwrite(&t1, sizeof(t1), 1, f); + size_t c2 = fwrite(&t2, sizeof(t2), 1, f); + + /* better error handling would be nice */ + assert(c0); + assert(c1); + assert(c2); + } + + fclose(f); + free(ctx.buf); +} + +void check_shift(tri_t shmt) +{ + int64_t v = tri_from(shmt); + assert(v < 27); +} + +tri_t check_nop(const char *nop) +{ + tri_t r = 0; + size_t count = 0; + size_t len = strlen(nop); + for (size_t i = 0; i < len; ++i) { + switch (nop[i]) { + case 'o': + case 'O': + case '0': tri_set_trit(r, i, 0); break; + + case 'n': + case 'N': + case 'i': tri_set_trit(r, i, -1); break; + + case 'p': + case 'P': + case '1': tri_set_trit(r, i, 1); break; + + case ' ': continue; + default: + fprintf(stderr, "invalid character in unop format: %c\n", nop[i]); + abort(); + } + + count++; + } + + assert(count == 3); + return r; +} + +tri_t check_nop3(const char *nop) +{ + tri_t r = 0; + size_t count = 0; + size_t len = strlen(nop); + for (size_t i = 0; i < len; ++i) { + switch (nop[i]) { + case 'o': + case 'O': + case '0': tri_set_trit(r, i, 0); break; + + case 'n': + case 'N': + case 'i': tri_set_trit(r, i, -1); break; + + case 'p': + case 'P': + case '1': tri_set_trit(r, i, 1); break; + + case ' ': continue; + default: + fprintf(stderr, "invalid character in unop format: %c\n", nop[i]); + abort(); + } + + count++; + } + + assert(count == 9); + return r; +} + +tri_t check_csr(const char *csr) +{ + /** @todo only mpower supported for now, will have to work on this */ + assert(strcmp(csr, "mpower")); + return 0; +} + +tri_t check_width(const char *width) +{ + if (strncmp(width, "w", 1) == 0) + return WIDTH_W; + + if (strncmp(width, "t", 1) == 0) + return WIDTH_T; + + fprintf(stderr, "illegal width specifier: %s\n", width); + abort(); + return 0; +} + +void emit_reloc(struct asm_ctx *ctx, enum asm_reloc reloc, const char *name) +{ + /** @todo implement */ +} + +void emit_label(struct asm_ctx *ctx, const char *name) +{ + /** @todo implement */ +} diff --git a/tasm/src/bits.c b/tasm/src/bits.c new file mode 100644 index 0000000..6397e2f --- /dev/null +++ b/tasm/src/bits.c @@ -0,0 +1,75 @@ +#include <tasm/bits.h> + +tri_t parse_opcode(tri_t i) +{ + /* opcode is lowest five trits */ + return tri_mask(i, 5); +} + +tri_t build_r(tri_t opcode, tri_t rd, tri_t fn0, tri_t rs1, tri_t rs2, tri_t fn5) +{ + return opcode + | tri_sl(rd , 5) + | tri_sl(fn0, 9) + | tri_sl(rs1, 14) + | tri_sl(rs2, 18) + | tri_sl(fn5, 22); +} + +void parse_r(tri_t i, tri_t *rd, tri_t *fn0, tri_t *rs1, tri_t *rs2, tri_t *fn5) +{ + *rd = tri_mask(tri_sr(i, 5), 4); + *fn0 = tri_mask(tri_sr(i, 9), 5); + *rs1 = tri_mask(tri_sr(i, 14), 4); + *rs2 = tri_mask(tri_sr(i, 18), 4); + *fn5 = tri_mask(tri_sr(i, 22), 5); +} + +tri_t build_i(tri_t opcode, tri_t rd, tri_t fn0, tri_t rs1, tri_t imm9) +{ + return opcode + | tri_sl(rd , 5) + | tri_sl(fn0 , 9) + | tri_sl(rs1 , 14) + | tri_sl(imm9, 18); +} + +void parse_i(tri_t i, tri_t *rd, tri_t *fn0, tri_t *rs1, tri_t *imm9) +{ + *rd = tri_mask(tri_sl(i, 5), 4); + *fn0 = tri_mask(tri_sl(i, 9), 5); + *rs1 = tri_mask(tri_sl(i, 14), 4); + *imm9 = tri_mask(tri_sl(i, 18), 9); +} + +tri_t build_s(tri_t opcode, tri_t imm4, tri_t fn0, tri_t rs1, tri_t rs2, tri_t imm5) +{ + return opcode + | tri_sl(imm4, 5) + | tri_sl(fn0 , 9) + | tri_sl(rs1 , 14) + | tri_sl(rs2 , 18) + | tri_sl(imm5, 22); +} + +void parse_s(tri_t i, tri_t *imm4, tri_t *fn0, tri_t *rs1, tri_t *rs2, tri_t *imm5) +{ + *imm4 = tri_mask(tri_sl(i, 5), 4); + *fn0 = tri_mask(tri_sl(i, 9), 5); + *rs1 = tri_mask(tri_sl(i, 14), 4); + *rs2 = tri_mask(tri_sl(i, 18), 4); + *imm5 = tri_mask(tri_sl(i, 22), 5); +} + +tri_t build_u(tri_t opcode, tri_t rd, tri_t imm18) +{ + return opcode + | tri_sl(rd , 5) + | tri_sl(imm18, 9); +} + +void parse_u(tri_t i, tri_t *rd, tri_t *imm18) +{ + *rd = tri_mask(tri_sl(i, 5), 4); + *imm18 = tri_mask(tri_sl(i, 9), 18); +} diff --git a/tasm/src/lexer.l b/tasm/src/lexer.l new file mode 100644 index 0000000..01a4bee --- /dev/null +++ b/tasm/src/lexer.l @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: copyleft-next-0.3.1 */ +/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ + +%option reentrant noyywrap nounput noinput nodefault +%{ +#define FROM_LEXER +#include <tasm/parser.h> + +static void update_yylloc(struct parser *parser, YYLTYPE *lloc, const char *text) +{ + (void)parser; + + lloc->first_line = lloc->last_line; + lloc->first_column = lloc->last_column; + + for (size_t i = 0; text[i] != 0; ++i) { + if (text[i] == '\n') { + lloc->last_line++; + /* flex uses 1 based indexing */ + lloc->last_column = 1; + } else { + lloc->last_column++; + } + } +} + +#define YY_USER_ACTION update_yylloc(parser, yylloc, yytext); +%} + +%x SC_COMMENT + +id [_a-zA-Z][_a-zA-Z0-9]* +label {id}[[:space:]]*: + +dec -?[0-9]+ +tri 0t[01i]+ +hept 0h[0-9A-HKMNPRTVXZa-hkmnprtvxz]+ +imm {tri}|{hept}|{dec} + +str \"(\\.|[^"\\])*\" + +%% + +{label} { + /* strip trailing ':' */ + char *s = yytext + strlen(yytext); + s[-1] = '\0'; + + yylval->str = yytext; + return label; +} + +{imm} { + yylval->tri = parse_imm(yytext); + return imm; +} + +{str} { + /* strip leading and trailing " */ + char *s = yytext + strlen(yytext); + s[-1] = '\0'; + + yylval->str = yytext + 1; + return str; +} + +".set" {return set;} +".include" {return include;} + +"x0" {return x0;} +"x1" {return x1;} +"x2" {return x2;} +"x3" {return x3;} +"x4" {return x4;} +"x5" {return x5;} +"x6" {return x6;} +"x7" {return x7;} +"x8" {return x8;} +"x9" {return x9;} +"x10" {return x10;} +"x11" {return x11;} +"x12" {return x12;} +"x13" {return x13;} +"x14" {return x14;} +"x15" {return x15;} +"x16" {return x16;} +"x17" {return x17;} +"x18" {return x18;} +"x19" {return x19;} +"x20" {return x20;} +"x21" {return x21;} +"x22" {return x22;} +"x23" {return x23;} +"x24" {return x24;} +"x25" {return x25;} +"x26" {return x26;} +"x27" {return x27;} +"x28" {return x28;} +"x29" {return x29;} +"x30" {return x30;} +"x31" {return x31;} +"x32" {return x32;} +"x33" {return x33;} +"x34" {return x34;} +"x35" {return x35;} +"x36" {return x36;} +"x37" {return x37;} +"x38" {return x38;} +"x39" {return x39;} +"x40" {return x40;} +"x41" {return x41;} +"x42" {return x42;} +"x43" {return x43;} +"x44" {return x44;} +"x45" {return x45;} +"x46" {return x46;} +"x47" {return x47;} +"x48" {return x48;} +"x49" {return x49;} +"x50" {return x50;} +"x51" {return x51;} +"x52" {return x52;} +"x53" {return x53;} +"x54" {return x54;} +"x55" {return x55;} +"x56" {return x56;} +"x57" {return x57;} +"x58" {return x58;} +"x59" {return x59;} +"x60" {return x60;} +"x61" {return x61;} +"x62" {return x62;} +"x63" {return x63;} +"x64" {return x64;} +"x65" {return x65;} +"x66" {return x66;} +"x67" {return x67;} +"x68" {return x68;} +"x69" {return x69;} +"x70" {return x70;} +"x71" {return x71;} +"x72" {return x72;} +"x73" {return x73;} +"x74" {return x74;} +"x75" {return x75;} +"x76" {return x76;} +"x77" {return x77;} +"x78" {return x78;} +"x79" {return x79;} +"x80" {return x80;} + +"addi" {return addi; /* i */} +"slti" {return slti;} +"sgei" {return sgei;} +"seqi" {return seqi;} +"snei" {return snei;} +"unop" {return unop;} +"diop" {return diop;} +"slli" {return slli;} +"srli" {return srli;} +"sll" {return sll;} +"srl" {return srl;} +"add" {return add;} +"sub" {return sub;} +"slt" {return slt;} +"sge" {return sge;} +"seq" {return seq;} +"sne" {return sne;} +"lui" {return lui;} +"auipc" {return auipc;} +"jal" {return jal;} +"jalr" {return jalr;} +"beq" {return beq;} +"bne" {return bne;} +"blt" {return blt;} +"bge" {return bge;} +"ld" {return ld;} +"st" {return st;} +"ecall" {return ecall;} +"ebreak" {return ebreak;} +"pcall" {return pcall;} +"fence" {return fence;} +"nop" {return nop; /* meta */} +"li" {return li;} +"la" {return la;} + +"mul" {return mul; /* m */} +"div" {return diV;} +"rem" {return rem;} + +"stt" {return stt; /* a */} +"cst" {return cst;} +"ent" {return ent;} +"cat" {return cat;} + +"csrrw" {return csrrw; /* Zcsr */} +"csrrs" {return csrrs;} +"csrrc" {return csrrc;} + +"//".* {/* skip line comments */} + +"/*" {BEGIN(SC_COMMENT);} +<SC_COMMENT>{ + "/*" {++parser->comment_nesting;} + "*"+"/" { + if (parser->comment_nesting) + --parser->comment_nesting; + else + BEGIN(INITIAL); + } + + "*"+ {} + [^/*\n]+ {} + [/] {} + \n {} +} + +{id} { + yylval->str = yytext; + return id; +} + +"(" {return LPAREN;} +")" {return RPAREN;} +"," {return COMMA;} + +[[:space:]]+ {/* skip whitespace */} + +. { + struct src_issue issue; + issue.loc = to_src_loc(yylloc); + issue.fctx.fbuf = parser->buf; + issue.fctx.fname = parser->fname; + src_issue(issue, "unexpected token: %s", yytext); + parser->failed = true; +} +%% diff --git a/tasm/src/main.c b/tasm/src/main.c new file mode 100644 index 0000000..bb6a1ac --- /dev/null +++ b/tasm/src/main.c @@ -0,0 +1,68 @@ +#include <tasm/assembler.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +static const char *cmdline_usage = +"tasm trinary assembler usage:\n" +" tasm [-I <dir>...] -o outfile infile\n" +" -h Show usage (this)\n" +" -I <dir> Add directory to include path\n" +" -o <outfile> File to output to\n" +" infile Top file to assemble\n" +; + +static void usage() +{ + fprintf(stderr, cmdline_usage); +} + +int main(int argc, char *argv[]) +{ + const char *outfile = NULL; + int opt; + while ((opt = getopt(argc, argv, "hI:o:")) != -1) { + switch (opt) { + case 'I': + fprintf(stderr, "include paths not yet implemented\n"); + break; + + case 'o': + outfile = optarg; + break; + + case 'h': + usage(); + exit(EXIT_SUCCESS); + break; + + default: + usage(); + exit(EXIT_FAILURE); + break; + } + } + + if (!outfile) { + fprintf(stderr, "no output file\n"); + usage(); + exit(EXIT_FAILURE); + } + + if (optind >= argc) { + fprintf(stderr, "no input files\n"); + usage(); + exit(EXIT_FAILURE); + } + + if (optind != argc - 1) { + fprintf(stderr, "too many input files\n"); + usage(); + exit(EXIT_FAILURE); + } + + if (assemble(outfile, argv[optind])) + exit(EXIT_FAILURE); + + return EXIT_SUCCESS; +} diff --git a/tasm/src/parser.y b/tasm/src/parser.y new file mode 100644 index 0000000..ef8d3df --- /dev/null +++ b/tasm/src/parser.y @@ -0,0 +1,532 @@ +/* SPDX-License-Identifier: copyleft-next-0.3.1 */ +/* Copyright 2023 Kim Kuparinen < kimi.h.kuparinen@gmail.com > */ + +%{ + +/* get access to fileno to avoid warnings with flex */ +#define _POSIX_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include <tasm/assembler.h> +#include <tasm/parser.h> +#include <tasm/bits.h> +#include <tasm/regs.h> +#include <tri.h> + +%} + +%locations + +%define parse.trace +%define parse.error verbose +%define api.pure full +%define lr.type ielr + +%lex-param {void *scanner} {struct parser *parser} +%parse-param {void *scanner} {struct parser *parser} {struct asm_ctx *ctx} + +%union { + const char *str; + enum gpr_num gpr; + tri_t tri; +}; + +%token <tri> imm +%token <str> label +%token <str> str +%token <str> id + +%token COMMA "," +%token LPAREN "(" +%token RPAREN ")" + +%token set +%token include +%nterm <gpr> gpr; +%nterm <str> width addr csr + +/* regs */ +%token x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 +%token x10 x11 x12 x13 x14 x15 x16 x17 x18 x19 +%token x20 x21 x22 x23 x24 x25 x26 x27 x28 x29 +%token x30 x31 x32 x33 x34 x35 x36 x37 x38 x39 +%token x40 x41 x42 x43 x44 x45 x46 x47 x48 x49 +%token x50 x51 x52 x53 x54 x55 x56 x57 x58 x59 +%token x60 x61 x62 x63 x64 x65 x66 x67 x68 x69 +%token x70 x71 x72 x73 x74 x75 x76 x77 x78 x79 +%token x80 + +/* i */ +%token addi slti sgei seqi snei +%token unop diop +%token slli srli sll srl +%token add sub +%token slt sge seq sne +%token lui auipc +%token jal jalr +%token beq bne blt bge +%token ld st +%token ecall ebreak pcall +%token fence +/* meta */ +%token li la nop + +/* m (avoid name clash with stdlib div) */ +%token mul diV rem + +/* a */ +%token stt cst ent cat + +/* Zcsr */ +%token csrrw csrrs csrrc + +%{ + +struct file_ctx { + const char *fname; + const char *fbuf; +}; + +struct src_loc { + int first_line; + int last_line; + int first_col; + int last_col; +}; + +struct src_issue { + struct src_loc loc; + struct file_ctx fctx; +}; + +/** Modifies the signature of yylex to fit our parser better. */ +#define YY_DECL int yylex(YYSTYPE *yylval, YYLTYPE *yylloc, \ + void *yyscanner, struct parser *parser) + +/** + * Declare yylex. + * + * @param yylval Bison current value. + * @param yylloc Bison location info. + * @param yyscanner Flex scanner. + * @param parser Current parser state. + * @return \c 0 when succesful, \c 1 otherwise. + * More info on yylex() can be found in the flex manual. + */ +YY_DECL; + +/** + * Convert bison location info to our own source location info. + * + * @param yylloc Bison location info. + * @return Internal location info. + */ +static struct src_loc to_src_loc(YYLTYPE *yylloc); + +/** + * Print parsing error. + * Automatically called by bison. + * + * @param yylloc Location of error. + * @param lexer Lexer. + * @param parser Parser state. + * @param msg Message to print. + */ +static void yyerror(YYLTYPE *yylloc, void *lexer, + struct parser *parser, struct asm_ctx *ctx, const char *msg); + +%} + +%start input; +%% + +width + : id + +/* eventually we might allow jumping to immediate addresses, but for now require + * that a label always be used */ +addr + : id + +/* lexer makes sure the abi names are correct */ +gpr + : x0 {$$ = X0_NUM;} + | x1 {$$ = X1_NUM;} + | x2 {$$ = X2_NUM;} + | x3 {$$ = X3_NUM;} + | x4 {$$ = X4_NUM;} + | x5 {$$ = X5_NUM;} + | x6 {$$ = X6_NUM;} + | x7 {$$ = X7_NUM;} + | x8 {$$ = X8_NUM;} + | x9 {$$ = X9_NUM;} + | x10 {$$ = X10_NUM;} + | x11 {$$ = X11_NUM;} + | x12 {$$ = X12_NUM;} + | x13 {$$ = X13_NUM;} + | x14 {$$ = X14_NUM;} + | x15 {$$ = X15_NUM;} + | x16 {$$ = X16_NUM;} + | x17 {$$ = X17_NUM;} + | x18 {$$ = X18_NUM;} + | x19 {$$ = X19_NUM;} + | x20 {$$ = X20_NUM;} + | x21 {$$ = X21_NUM;} + | x22 {$$ = X22_NUM;} + | x23 {$$ = X23_NUM;} + | x24 {$$ = X24_NUM;} + | x25 {$$ = X25_NUM;} + | x26 {$$ = X26_NUM;} + | x27 {$$ = X27_NUM;} + | x28 {$$ = X28_NUM;} + | x29 {$$ = X29_NUM;} + | x30 {$$ = X30_NUM;} + | x31 {$$ = X31_NUM;} + | x32 {$$ = X32_NUM;} + | x33 {$$ = X33_NUM;} + | x34 {$$ = X34_NUM;} + | x35 {$$ = X35_NUM;} + | x36 {$$ = X36_NUM;} + | x37 {$$ = X37_NUM;} + | x38 {$$ = X38_NUM;} + | x39 {$$ = X39_NUM;} + | x40 {$$ = X40_NUM;} + | x41 {$$ = X41_NUM;} + | x42 {$$ = X42_NUM;} + | x43 {$$ = X43_NUM;} + | x44 {$$ = X44_NUM;} + | x45 {$$ = X45_NUM;} + | x46 {$$ = X46_NUM;} + | x47 {$$ = X47_NUM;} + | x48 {$$ = X48_NUM;} + | x49 {$$ = X49_NUM;} + | x50 {$$ = X50_NUM;} + | x51 {$$ = X51_NUM;} + | x52 {$$ = X52_NUM;} + | x53 {$$ = X53_NUM;} + | x54 {$$ = X54_NUM;} + | x55 {$$ = X55_NUM;} + | x56 {$$ = X56_NUM;} + | x57 {$$ = X57_NUM;} + | x58 {$$ = X58_NUM;} + | x59 {$$ = X59_NUM;} + | x60 {$$ = X60_NUM;} + | x61 {$$ = X61_NUM;} + | x62 {$$ = X62_NUM;} + | x63 {$$ = X63_NUM;} + | x64 {$$ = X64_NUM;} + | x65 {$$ = X65_NUM;} + | x66 {$$ = X66_NUM;} + | x67 {$$ = X67_NUM;} + | x68 {$$ = X68_NUM;} + | x69 {$$ = X69_NUM;} + | x70 {$$ = X70_NUM;} + | x71 {$$ = X71_NUM;} + | x72 {$$ = X72_NUM;} + | x73 {$$ = X73_NUM;} + | x74 {$$ = X74_NUM;} + | x75 {$$ = X75_NUM;} + | x76 {$$ = X76_NUM;} + | x77 {$$ = X77_NUM;} + | x78 {$$ = X78_NUM;} + | x79 {$$ = X79_NUM;} + | x80 {$$ = X80_NUM;} + +i + : addi gpr "," gpr "," imm + {emit_i(ctx, OPCODE_OP_IMM, $2, OP_IMM_ADDI, $4, $6);} + + | slti gpr "," gpr "," imm + {emit_i(ctx, OPCODE_OP_IMM, $2, OP_IMM_SLTI, $4, $6);} + + | sgei gpr "," gpr "," imm + {emit_i(ctx, OPCODE_OP_IMM, $2, OP_IMM_SGEI, $4, $6);} + + | seqi gpr "," gpr "," imm + {emit_i(ctx, OPCODE_OP_IMM, $2, OP_IMM_SEQI, $4, $6);} + + | snei gpr "," gpr "," imm + {emit_i(ctx, OPCODE_OP_IMM, $2, OP_IMM_SNEI, $4, $6);} + + | slli gpr "," gpr "," imm + {check_shift($6); emit_i(ctx, OPCODE_OP_IMM, $2, OP_IMM_SLLI, $4, $6);} + + | srli gpr "," gpr "," imm + {check_shift($6); emit_i(ctx, OPCODE_OP_IMM, $2, OP_IMM_SRLI, $4, $6);} + + | add gpr "," gpr "," gpr + {emit_r(ctx, OPCODE_OP, $2, OP_ADD, $4, $6, 0);} + + | sub gpr "," gpr "," gpr + {emit_r(ctx, OPCODE_OP, $2, OP_SUB, $4, $6, 0);} + + | slt gpr "," gpr "," gpr + {emit_r(ctx, OPCODE_OP, $2, OP_SLT, $4, $6, 0);} + + | sge gpr "," gpr "," gpr + {emit_r(ctx, OPCODE_OP, $2, OP_SGE, $4, $6, 0);} + + | seq gpr "," gpr "," gpr + {emit_r(ctx, OPCODE_OP, $2, OP_SEQ, $4, $6, 0);} + + | sne gpr "," gpr "," gpr + {emit_r(ctx, OPCODE_OP, $2, OP_SNE, $4, $6, 0);} + + | sll gpr "," gpr "," gpr + {emit_r(ctx, OPCODE_OP, $2, OP_SLL, $4, $6, 0);} + + | srl gpr "," gpr "," gpr + {emit_r(ctx, OPCODE_OP, $2, OP_SRL, $4, $6, 0);} + + | lui gpr "," imm + {emit_u(ctx, OPCODE_LUI, $2, $4);} + + | auipc gpr "," imm + {emit_u(ctx, OPCODE_AUIPC, $2, $4);} + + | jal gpr "," addr + {emit_u(ctx, OPCODE_JAL, $2, 0); + emit_reloc(ctx, RELOC_J, $4);} + + | jalr gpr "," gpr "," imm + {emit_i(ctx, OPCODE_JALR, $2, 0, $4, $6);} + + | beq gpr "," gpr "," addr + {emit_s(ctx, OPCODE_BRANCH, BRANCH_BEQ, $2, $4, 0); + emit_reloc(ctx, RELOC_B, $6);} + + | bne gpr "," gpr "," addr + {emit_s(ctx, OPCODE_BRANCH, BRANCH_BNE, $2, $4, 0); + emit_reloc(ctx, RELOC_B, $6);} + + | blt gpr "," gpr "," addr + {emit_s(ctx, OPCODE_BRANCH, BRANCH_BLT, $2, $4, 0); + emit_reloc(ctx, RELOC_B, $6);} + + | bge gpr "," gpr "," addr + {emit_s(ctx, OPCODE_BRANCH, BRANCH_BGE, $2, $4, 0); + emit_reloc(ctx, RELOC_B, $6);} + + | ld width "," gpr "," imm "(" gpr ")" + {emit_i(ctx, OPCODE_LOAD, check_width($2), $4, $8, $6);} + + | st width "," gpr "," imm "(" gpr ")" + {emit_s(ctx, OPCODE_STORE, check_width($2), $4, $8, $6);} + + | ecall + {emit_i(ctx, OPCODE_SYSTEM, X0_NUM, SYSTEM_ECALL, X0_NUM, 0);} + + | ebreak + {emit_i(ctx, OPCODE_SYSTEM, X0_NUM, SYSTEM_EBREAK, X0_NUM, 0);} + + | pcall + {emit_i(ctx, OPCODE_SYSTEM, X0_NUM, SYSTEM_PCALL, X0_NUM, 0);} + + | fence + {emit_i(ctx, OPCODE_MEM, X0_NUM, MEM_FENCE, X0_NUM, 0); /* still todo */} + + | unop gpr "," gpr "," str + {emit_i(ctx, OPCODE_OP_IMM, $2, OP_IMM_UNOP, $4, check_nop($6));} + + | diop gpr "," gpr "," gpr "," str + {emit_d(ctx, OPCODE_DIOP, $2, $4, $6, check_nop3($8));} + + /* meta */ + | nop + {emit_i(ctx, OPCODE_OP_IMM, X0_NUM, OP_IMM_ADDI, X0_NUM, 0);} + + | la gpr "," addr + {emit_u(ctx, OPCODE_LUI, $2, 0); + /* important that the reloc is in the middle here */ + emit_reloc(ctx, RELOC_LA, $4); + emit_i(ctx, OPCODE_OP_IMM, $2, OP_IMM_ADDI, $2, 0);} + + | li gpr "," imm + {emit_u(ctx, OPCODE_LUI, $2, 0); + emit_i(ctx, OPCODE_OP_IMM, $2, OP_IMM_ADDI, $2, 0);} + /* stuff like call and ret TBD once I've come up with a proper register + * calling convention so we know which register to use as ra */ + +m + : mul gpr "," gpr "," gpr + {emit_r(ctx, OPCODE_OP, $2, OP_MUL, $4, $6, 0);} + + | diV gpr "," gpr "," gpr + {emit_r(ctx, OPCODE_OP, $2, OP_DIV, $4, $6, 0);} + + | rem gpr "," gpr "," gpr + {emit_r(ctx, OPCODE_OP, $2, OP_DIV, $4, $6, 0);} + +a + : stt + {emit_i(ctx, OPCODE_MEM, X0_NUM, MEM_STT, X0_NUM, 0);} + + | cst width "," gpr "," imm "(" gpr ")" + {emit_s(ctx, OPCODE_STORE, STORE_CST | check_width($2), $4, $8, $6);} + + | ent gpr + {emit_i(ctx, OPCODE_MEM, $2, MEM_ENT, X0_NUM, 0);} + + | cat + {emit_i(ctx, OPCODE_MEM, X0_NUM, MEM_CAT, X0_NUM, 0);} + +Zcsr + : csrrw csr "," gpr "," gpr + {emit_i(ctx, OPCODE_SYSTEM, $4, SYSTEM_CSRRW, $6, check_csr($2));} + + | csrrs csr "," gpr "," gpr + {emit_i(ctx, OPCODE_SYSTEM, $4, SYSTEM_CSRRS, $6, check_csr($2));} + + | csrrc csr "," gpr "," gpr + {emit_i(ctx, OPCODE_SYSTEM, $4, SYSTEM_CSRRC, $6, check_csr($2));} + +csr + : id + +statement + : i + | a + | m + | Zcsr + +directive + : set id imm + | include str {process_file(ctx, $2);} + +top + : label {emit_label(ctx, $1);} + | statement + | directive + +unit + : top + | top unit + +input + : unit + | /* empty */ + +%% + +static const char *find_lineno(const char *buf, size_t no) +{ + if (no == 0 || no == 1) + return buf; + + char c; + while ((c = *buf)) { + buf++; + + if (c == '\n') + no--; + + if (no == 1) + break; + } + + return buf; +} + +static void _issue(struct src_issue issue, const char *fmt, va_list args) +{ + /* get start and end of current line in buffer */ + const char *line_start = find_lineno(issue.fctx.fbuf, + issue.loc.first_line); + const char *line_end = strchr(line_start, '\n'); + if (!line_end) + line_end = strchr(line_start, 0); + + const int line_len = line_end - line_start; + + fprintf(stderr, "%s:%i:%i: ", + issue.fctx.fname, + issue.loc.first_line, + issue.loc.first_col); + + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + + int lineno_len = snprintf(NULL, 0, "%i", issue.loc.first_line); + fputc(' ', stderr); + fprintf(stderr, "%i | ", issue.loc.first_line); + + fprintf(stderr, "%.*s\n", line_len, line_start); + + for (int i = 0; i < lineno_len + 2; ++i) + fputc(' ', stderr); + + fprintf(stderr, "| "); + + for (int i = 0; i < issue.loc.first_col - 1; ++i) + fputc(line_start[i] == '\t' ? '\t' : ' ', stderr); + + for (int i = issue.loc.first_col; i < issue.loc.last_col; ++i) { + if (i == issue.loc.first_col) + fputc('^', stderr); + else + fputc('~', stderr); + } + + fputc('\n', stderr); +} + +void src_issue(struct src_issue issue, const char *err_msg, ...) +{ + va_list args; + va_start(args, err_msg); + _issue(issue, err_msg, args); + va_end(args); +} +#include "gen_lexer.inc" + +static struct src_loc to_src_loc(YYLTYPE *yylloc) +{ + struct src_loc loc; + loc.first_line = yylloc->first_line; + loc.last_line = yylloc->last_line; + loc.first_col = yylloc->first_column; + loc.last_col = yylloc->last_column; + return loc; +} + +static void yyerror(YYLTYPE *yylloc, void *lexer, + struct parser *parser, struct asm_ctx *ctx, const char *msg) +{ + (void)lexer; + (void)ctx; + + struct src_issue issue; + issue.loc = to_src_loc(yylloc); + issue.fctx.fbuf = parser->buf; + issue.fctx.fname = parser->fname; + src_issue(issue, msg); +} + +struct parser *create_parser() +{ + return calloc(1, sizeof(struct parser)); +} + +void destroy_parser(struct parser *p) +{ + yylex_destroy(p->lexer); + free(p); +} + +void parse(struct parser *p, struct asm_ctx *ctx, const char *fname, const char *buf) +{ + p->fname = fname; + p->buf = buf; + + p->comment_nesting = 0; + + p->failed = false; + + yylex_init(&p->lexer); + yy_scan_string(buf, p->lexer); + yyparse(p->lexer, p, ctx); +} diff --git a/tasm/src/source.mk b/tasm/src/source.mk new file mode 100644 index 0000000..5052ba3 --- /dev/null +++ b/tasm/src/source.mk @@ -0,0 +1,8 @@ +SOURCES != echo src/*.c +TASM_SOURCES += $(SOURCES) gen/gen_parser.c + +gen/gen_parser.c: src/parser.y gen/gen_lexer.inc + bison -Wcounterexamples -o gen/gen_parser.c src/parser.y + +gen/gen_lexer.inc: src/lexer.l + flex -o gen/gen_lexer.inc src/lexer.l |
