This commit is contained in:
ZZY
2025-04-01 00:13:21 +08:00
parent 2b4857001c
commit 74f43a1ab7
79 changed files with 2271 additions and 2861 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
.vscode/ .*
!.gitignore
# smcc compiler generated files # smcc compiler generated files
*.bin *.bin

View File

@ -0,0 +1,18 @@
CC = gcc
AR = ar
CFLAGS = -g -Wall -I../..
# 自动收集所有子模块源文件
EXCLUDE = test*.c
SRCS = $(filter-out $(EXCLUDE), $(wildcard *.c))
OBJS = $(SRCS:.c=.o)
libasm.a: $(OBJS)
$(AR) rcs $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f libasm.a $(OBJS)

204
assembler/riscv32/riscv32.c Normal file
View File

@ -0,0 +1,204 @@
#include "riscv32.h"
#include "riscv32_def.h"
#include <lib/core.h>
static const rv32_instr_t rv32_instrs[] = {
[RV_LUI] = {RV32_I_EXT, RV_U_TYPE, RV_LUI, "lui", 0x37},
[RV_AUIPC] = {RV32_I_EXT, RV_U_TYPE, RV_AUIPC, "auipc", 0x17},
[RV_JAL] = {RV32_I_EXT, RV_J_TYPE, RV_JAL, "jal", 0x6F},
[RV_JALR] = {RV32_I_EXT, RV_I_TYPE, RV_JALR, "jalr", 0x67},
[RV_LB] = {RV32_I_EXT, RV_I_TYPE, RV_LB, "lb", 0x03 | RV_F3(0x0)},
[RV_LH] = {RV32_I_EXT, RV_I_TYPE, RV_LH, "lh", 0x03 | RV_F3(0x1)},
[RV_LW] = {RV32_I_EXT, RV_I_TYPE, RV_LW, "lw", 0x03 | RV_F3(0x2)},
[RV_LBU] = {RV32_I_EXT, RV_I_TYPE, RV_LBU, "lbu", 0x03 | RV_F3(0x4)},
[RV_LHU] = {RV32_I_EXT, RV_I_TYPE, RV_LHU, "lhu", 0x03 | RV_F3(0x5)},
[RV_ADDI] = {RV32_I_EXT, RV_I_TYPE, RV_ADDI, "addi", 0x13 | RV_F3(0x0)},
[RV_SLTI] = {RV32_I_EXT, RV_I_TYPE, RV_SLTI, "slti", 0x13 | RV_F3(0x2)},
[RV_SLTIU] = {RV32_I_EXT, RV_I_TYPE, RV_SLTIU, "sltiu", 0x13 | RV_F3(0x3)},
[RV_XORI] = {RV32_I_EXT, RV_I_TYPE, RV_XORI, "xori", 0x13 | RV_F3(0x4)},
[RV_ORI] = {RV32_I_EXT, RV_I_TYPE, RV_ORI, "ori", 0x13 | RV_F3(0x6)},
[RV_ANDI] = {RV32_I_EXT, RV_I_TYPE, RV_ANDI, "andi", 0x13 | RV_F3(0x7)},
[RV_SLLI] = {RV32_I_EXT, RV_I_TYPE, RV_SLLI, "slli", 0x13 | RV_F3(0x0)},
[RV_SRLI] = {RV32_I_EXT, RV_I_TYPE, RV_SRLI, "srli", 0x13 | RV_F3(0x5)},
[RV_SRAI] = {RV32_I_EXT, RV_I_TYPE, RV_SRAI, "srai", 0x13 | RV_F3(0x5)},
[RV_BEQ] = {RV32_I_EXT, RV_B_TYPE, RV_BEQ, "beq", 0x63 | RV_F3(0x0)},
[RV_BNE] = {RV32_I_EXT, RV_B_TYPE, RV_BNE, "bne", 0x63 | RV_F3(0x1)},
[RV_BLT] = {RV32_I_EXT, RV_B_TYPE, RV_BLT, "blt", 0x63 | RV_F3(0x4)},
[RV_BGE] = {RV32_I_EXT, RV_B_TYPE, RV_BGE, "bge", 0x63 | RV_F3(0x5)},
[RV_BLTU] = {RV32_I_EXT, RV_B_TYPE, RV_BLTU, "bltu", 0x63 | RV_F3(0x6)},
[RV_BGEU] = {RV32_I_EXT, RV_B_TYPE, RV_BGEU, "bgeu", 0x63 | RV_F3(0x7)},
[RV_SB] = {RV32_I_EXT, RV_S_TYPE, RV_SB, "sb", 0x23 | RV_F3(0x0)},
[RV_SH] = {RV32_I_EXT, RV_S_TYPE, RV_SH, "sh", 0x23 | RV_F3(0x1)},
[RV_SW] = {RV32_I_EXT, RV_S_TYPE, RV_SW, "sw", 0x23 | RV_F3(0x2)},
[RV_ADD] = {RV32_I_EXT, RV_R_TYPE, RV_ADD, "add", 0x33 | RV_F3(0x0) | RV_F7(0x00)},
[RV_SUB] = {RV32_I_EXT, RV_R_TYPE, RV_SUB, "sub", 0x33 | RV_F3(0x0) | RV_F7(0x20)},
[RV_SLL] = {RV32_I_EXT, RV_R_TYPE, RV_SLL, "sll", 0x33 | RV_F3(0x1) | RV_F7(0x00)},
[RV_SLT] = {RV32_I_EXT, RV_R_TYPE, RV_SLT, "slt", 0x33 | RV_F3(0x2) | RV_F7(0x00)},
[RV_SLTU] = {RV32_I_EXT, RV_R_TYPE, RV_SLTU, "sltu", 0x33 | RV_F3(0x3) | RV_F7(0x00)},
[RV_XOR] = {RV32_I_EXT, RV_R_TYPE, RV_XOR, "xor", 0x33 | RV_F3(0x4) | RV_F7(0x00)},
[RV_SRL] = {RV32_I_EXT, RV_R_TYPE, RV_SRL, "srl", 0x33 | RV_F3(0x5) | RV_F7(0x00)},
[RV_SRA] = {RV32_I_EXT, RV_R_TYPE, RV_SRA, "sra", 0x33 | RV_F3(0x5) | RV_F7(0x20)},
[RV_OR] = {RV32_I_EXT, RV_R_TYPE, RV_OR, "or", 0x33 | RV_F3(0x6) | RV_F7(0x00)},
[RV_AND] = {RV32_I_EXT, RV_R_TYPE, RV_AND, "and", 0x33 | RV_F3(0x7) | RV_F7(0x00)},
// {RV32_I_EXT, RV_I_TYPE, RV_FENCE, "fence", 0x0F},
[RV_ECALL] = {RV32_I_EXT, RV_I_TYPE, RV_ECALL, "ecall", 0x73},
[RV_EBREAK] = {RV32_I_EXT, RV_I_TYPE, RV_EBREAK,"ebreak",0x100073},
[RV_MUL] = {RV32_M_EXT, RV_R_TYPE, RV_MUL, "mul", 0x33 | RV_F3(0x0) | RV_F7(0x01)},
[RV_DIV] = {RV32_M_EXT, RV_R_TYPE, RV_DIV, "div", 0x33 | RV_F3(0x0) | RV_F7(0x05)},
[RV_REM] = {RV32_M_EXT, RV_R_TYPE, RV_REM, "rem", 0x33 | RV_F3(0x0) | RV_F7(0x07)},
};
void init_rv32_prog(rv32_prog_t* prog, strpool_t* strpool) {
if (strpool == NULL) {
prog->strpool = salloc_alloc(sizeof(strpool_t));
init_strpool(prog->strpool);
} else {
prog->strpool = strpool;
}
prog->data_base_address = 0;
vector_init(prog->data);
prog->text_base_address = 0;
vector_init(prog->text);
vector_init(prog->rinstrs);
init_symtab_asm(&prog->symtab);
}
static inline int valid_reg(rv_reg_t reg) {
// return reg >= REG_X0 && reg <= REG_X31;
return !(reg & ~0x1F);
}
// static int valid_instr
// (rv_instr_type_t type, u32_t imm, rv_reg_t rs1, rv_reg_t rs2, rv_reg_t rd) {
// Assert(type >= 0 && type < RV_INSTR_TYPE_COUNT);
// rv_fmt_t fmt = rv32_instrs[type].format;
// switch (fmt) {
// case RV_R_TYPE:
// if (valid_reg(rd) && valid_reg(rs1) && valid_reg(rs2)) {
// return 1;
// }
// break;
// case RV_I_TYPE:
// if (valid_reg(rd) && valid_reg(rs1) && rs2 == 0 && (imm & ~0xFFF) == 0) {
// return 1;
// }
// break;
// case RV_S_TYPE:
// if (rd == 0 && valid_reg(rs1) && valid_reg(rs2) && (imm & ~0xFFF) == 0) {
// return 1;
// }
// break;
// case RV_B_TYPE:
// if (rd == 0 && valid_reg(rs1) && valid_reg(rs2) && (imm & ~(0xFFF << 1)) == 0) {
// return 1;
// }
// break;
// case RV_U_TYPE:
// if (valid_reg(rd) && rs1 == 0 && rs2 == 0 && (imm & ~(0xFFFFF << 12)) == 0) {
// return 1;
// }
// break;
// case RV_J_TYPE:
// if (valid_reg(rd) && rs1 == 0 && rs2 == 0 && (imm & ~(0xFFFFF)) == 0) {
// return 1;
// }
// break;
// }
// return 0;
// }
int emit_rv32_instr(rv32_prog_t* prog, rv32_instr_t *instr, u32_t idx, symasm_entry_t *target) {
// TODO check out input data
instr->extention = rv32_instrs[instr->instr_type].extention;
instr->format = rv32_instrs[instr->instr_type].format;
instr->instr_name = rv32_instrs[instr->instr_type].instr_name;
instr->instr = rv32_instrs[instr->instr_type].instr;
switch (instr->format) {
case RV_R_TYPE:
instr->instr |= (instr->rd << 7) | (instr->rs1 << 15) | (instr->rs2 << 20);
break;
case RV_I_TYPE:
instr->instr |= (instr->rd << 7) | (instr->rs1 << 15) | (instr->imm << 20);
break;
case RV_S_TYPE:
instr->instr |= (instr->rs1 << 15) | (instr->rs2 << 20) |
((instr->imm & 0x1F) << 7) | ((instr->imm & 0xFE0) << 20);
break;
case RV_B_TYPE:
instr->instr |= (instr->rs1 << 15) | (instr->rs2 << 20) |
(((instr->imm) >> 12) & 0x1) << 31 | /* imm[12:12] -> instr[31:31] */ \
(((instr->imm) >> 5) & 0x3F) << 25 | /* imm[10:5] -> instr[30:25] */ \
(((instr->imm) >> 1) & 0xF) << 8 | /* imm[4:1] -> instr[11:8] */ \
(((instr->imm) >> 11) & 0x1) << 7; /* imm[11:11] -> instr[7:7] */
break;
case RV_U_TYPE:
instr->instr |= (instr->rd << 7) | (instr->imm & 0xFFFFF000);
break;
case RV_J_TYPE:
instr->instr |= (instr->rd << 7) |
(((instr->imm) >> 20) & 0x1) << 31 | /* imm[20:20] -> instr[31:31] */ \
(((instr->imm) >> 1) & 0x3FF)<< 21 | /* imm[10:1] -> instr[30:21] */ \
(((instr->imm) >> 11) & 0x1) << 20 | /* imm[11:11] -> instr[20:20] */ \
(((instr->imm) >> 12) & 0xFF) << 12; /* imm[19:12] -> instr[19:12] */
break;
default:
Panic("Invalid instruction format");
}
if (target != NULL) {
Assert(idx == -1);
rotated_instr_t rinstr = {
.instr = *instr,
.address = prog->text.size,
.target = *target,
};
vector_push(prog->rinstrs, rinstr);
// GOTO idx == -1
}
if (idx == -1) {
vector_push(prog->text, instr->instr);
return idx;
} else if (idx >= 0 && idx < prog->text.size) {
vector_at(prog->text, idx) = instr->instr;
return prog->text.size - 1;
} else {
Panic("Invalid instruction index");
}
return -1;
}
// int gen_rv32_instr(rv32_prog_t* prog) {
// vector_header(rinstrs_g, rotated_instr_t);
// vector_init(rinstrs_g);
// for (int i = 0; i < prog->rinstrs.size; i++) {
// rotated_instr_t* rinstr = &prog->rinstrs.data[i];
// // FIXME address typedef needed
// u32_t* ptr = symtab_get(&prog->symtab, &rinstr->target);
// if (ptr == NULL) {
// vector_push(rinstrs_g, *rinstr);
// continue;
// }
// u32_t target_address = *ptr;
// rv32_instr_t *back_instr = &rinstr->instr;
// // FIXME 重定向类别
// back_instr->imm = target_address;
// emit_rv32_instr(prog, back_instr, target_address, NULL);
// }
// vector_free(prog->rinstrs);
// prog->rinstrs.cap = rinstrs_g.cap;
// }

View File

@ -0,0 +1,30 @@
#ifndef __SMCC_ASM_RISCV32_H__
#define __SMCC_ASM_RISCV32_H__
#include <lib/core.h>
#include <lib/utils/ds/vector.h>
#include <lib/utils/symtab/symtab.h>
#include "riscv32_def.h"
#include "symtab_asm.h"
typedef struct rotated_instr {
u32_t address;
symasm_entry_t target;
rv32_instr_t instr;
} rotated_instr_t;
typedef struct rv32_prog {
strpool_t* strpool;
symtab_asm_t symtab;
u32_t text_base_address;
vector_header(text, u32_t);
u32_t data_base_address;
vector_header(data, iptr_t);
vector_header(rinstrs, rotated_instr_t);
} rv32_prog_t;
void init_rv32_prog(rv32_prog_t* prog, strpool_t* strpool);
int emit_rv32_instr(rv32_prog_t* prog, rv32_instr_t *instr, u32_t idx, symasm_entry_t *target);
// int gen_rv32_instr(rv32_prog_t* prog);
#endif

View File

@ -0,0 +1,119 @@
#ifndef __SMCC_RISCV32_DEF_H__
#define __SMCC_RISCV32_DEF_H__
#include <lib/core.h>
typedef enum rv_ext {
RV32_I_EXT,
RV32_M_EXT,
} rv_ext_t;
typedef enum rv_fmt {
RV_R_TYPE,
RV_I_TYPE,
RV_S_TYPE,
RV_B_TYPE,
RV_U_TYPE,
RV_J_TYPE,
} rv_fmt_t;
typedef enum rv_instr_type {
/* U type */
RV_AUIPC,
RV_LUI,
/* J type */
RV_JAL,
/* I type */
RV_JALR,
// Load
RV_LB,
RV_LH,
RV_LW,
RV_LBU,
RV_LHU,
// Imm arithmetic
RV_ADDI,
RV_SLTI,
RV_SLTIU,
RV_XORI,
RV_ORI,
RV_ANDI,
// Imm shift
RV_SLLI,
RV_SRLI,
RV_SRAI,
/* B type */
RV_BEQ,
RV_BNE,
RV_BLT,
RV_BGE,
RV_BLTU,
RV_BGEU,
/* S type */
RV_SB,
RV_SH,
RV_SW,
/* R type */
RV_ADD,
RV_SUB,
RV_SLL,
RV_SLT,
RV_SLTU,
RV_XOR,
RV_SRL,
RV_SRA,
RV_OR,
RV_AND,
/* I type (system) */
RV_FENCE,
RV_ECALL,
RV_EBREAK,
/* M-Extention */
RV_MUL,
RV_DIV,
RV_REM,
// RV_MULH,
// RV_DIVU,
// RV_REMU,
// RV_MULHSU,
// RV_MULHU,
// RV_DIVU,
// RV_REMU,
// RV_MULHSU,
// RV_MULHU,
RV_INSTR_TYPE_COUNT,
} rv_instr_type_t;
typedef enum rv_reg {
REG_X0, REG_X1, REG_X2, REG_X3, REG_X4, REG_X5, REG_X6, REG_X7,
REG_X8, REG_X9, REG_X10, REG_X11, REG_X12, REG_X13, REG_X14, REG_X15,
REG_X16, REG_X17, REG_X18, REG_X19, REG_X20, REG_X21, REG_X22, REG_X23,
REG_X24, REG_X25, REG_X26, REG_X27, REG_X28, REG_X29, REG_X30, REG_X31,
REG_ZERO = REG_X0, REG_RA = REG_X1, REG_SP = REG_X2, REG_GP = REG_X3,
REG_TP = REG_X4, REG_T0 = REG_X5, REG_T1 = REG_X6, REG_T2 = REG_X7,
REG_S0 = REG_X8, REG_S1 = REG_X9, REG_A0 = REG_X10, REG_A1 = REG_X11,
REG_A2 = REG_X12, REG_A3 = REG_X13, REG_A4 = REG_X14, REG_A5 = REG_X15,
REG_A6 = REG_X16, REG_A7 = REG_X17, REG_S2 = REG_X18, REG_S3 = REG_X19,
REG_S4 = REG_X20, REG_S5 = REG_X21, REG_S6 = REG_X22, REG_S7 = REG_X23,
REG_S8 = REG_X24, REG_S9 = REG_X25, REG_S10 = REG_X26, REG_S11 = REG_X27,
REG_T3 = REG_X28, REG_T4 = REG_X29, REG_T5 = REG_X30, REG_T6 = REG_X31,
REG_NULL = 0,
} rv_reg_t;
#define RV_F3(f3) (f3 << 12)
#define RV_F7(f7) (f7 << 25)
typedef struct rv32_instr {
rv_ext_t extention;
rv_fmt_t format;
rv_instr_type_t instr_type;
const char* instr_name;
u32_t instr;
u32_t imm;
rv_reg_t rd;
rv_reg_t rs1;
rv_reg_t rs2;
} rv32_instr_t;
#endif

View File

@ -0,0 +1,948 @@
#ifndef __SMCC_RISCV32_INSTR_H__
#define __SMCC_RISCV32_INSTR_H__
#include "riscv32.h"
#include "riscv32_def.h"
#include "symtab_asm.h"
#define EMIT_PUSH_BACK (-1)
/* base integer instruction */
/**
* ADD
*
* add rd, rs1, rs2
*
* rd = rs1 + rs2
*/
static inline int
rv32_add(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) {
rv32_instr_t instr = {
.instr_type = RV_ADD,
.rd = rd,
.rs1 = rs1,
.rs2 = rs2,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* ADD Immediate
*
* addi rd, rs1, imm
*
* rd = rs1 + imm (imm maybe cut)
*/
static inline int
rv32_addi(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_ADDI,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* AND
*
* and rd, rs1, rs2
*
* rd = rs1 & rs2
*/
static inline int
rv32_and(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) {
rv32_instr_t instr = {
.instr_type = RV_AND,
.rd = rd,
.rs1 = rs1,
.rs2 = rs2,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* AND Immediate
*
* andi rd, rs1, imm
*
* rd = rs1 & imm (imm maybe cut)
*/
static inline int
rv32_andi(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_ANDI,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Add Upper Immediate to PC
*
* auipc rd, imm{12}
*
* rd = PC + imm{12}
*/
static inline int
rv32_auipc(rv32_prog_t* prog, rv_reg_t rd, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_AUIPC,
.rd = rd,
.rs1 = REG_NULL,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Branch EQual
*
* beq rs1, rs2, imm
*
* if (rs1 == rs2) PC += imm{alian2}
*/
static inline int
rv32_beq(rv32_prog_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_BEQ,
.rd = REG_NULL,
.rs1 = rs1,
.rs2 = rs2,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Branch Greater than or Equal
*
* bge rs1, rs2, imm
*
* if (rs1 >= rs2) PC += imm{alian2}
*/
static inline int
rv32_bge(rv32_prog_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_BGE,
.rd = REG_NULL,
.rs1 = rs1,
.rs2 = rs2,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Branch Greater than or Equal Unsigned
*
* bgeu rs1, rs2, imm
*
* if (rs1 >= u rs2) PC += imm{alian2}
*/
static inline int
rv32_bgeu(rv32_prog_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_BGEU,
.rd = REG_NULL,
.rs1 = rs1,
.rs2 = rs2,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Branch Less Than
*
* blt rs1, rs2, imm
*
* if (rs1 < rs2) PC += imm{alian2}
*/
static inline int
rv32_blt(rv32_prog_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_BLT,
.rd = REG_NULL,
.rs1 = rs1,
.rs2 = rs2,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Branch Less Than Unsigned
*
* bltu rs1, rs2, imm
*
* if (rs1 < u rs2) PC += imm{alian2}
*/
static inline int
rv32_bltu(rv32_prog_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_BLTU,
.rd = REG_NULL,
.rs1 = rs1,
.rs2 = rs2,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Branch Not Equal
*
* bne rs1, rs2, imm
*
* if (rs1 != rs2) PC += imm{alian2}
*/
static inline int
rv32_bne(rv32_prog_t* prog, rv_reg_t rs1, rv_reg_t rs2, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_BNE,
.rd = REG_NULL,
.rs1 = rs1,
.rs2 = rs2,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Environment BREAK
*
* ebreak
*
* Transfer control to debugger
*/
static inline int
rv32_ebreak(rv32_prog_t* prog) {
rv32_instr_t instr = {
.instr_type = RV_EBREAK,
.rd = REG_NULL,
.rs1 = REG_NULL,
.rs2 = REG_NULL,
.imm = 1,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Environment CALL
*
* ecall
*
* Transfer control to operating system
*/
static inline int
rv32_ecall(rv32_prog_t* prog) {
rv32_instr_t instr = {
.instr_type = RV_ECALL,
.rd = REG_NULL,
.rs1 = REG_NULL,
.rs2 = REG_NULL,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Jump And Link
*
* jal rd, imm
*
* rd = PC + 4; PC += imm{alian2}
*/
static inline int
rv32_jal(rv32_prog_t* prog, rv_reg_t rd, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_JAL,
.rd = rd,
.rs1 = REG_NULL,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Jump And Link Register
*
* jalr rd, rs1, imm
*
* rd = PC + 4; PC = rs1 + imm
*/
static inline int
rv32_jalr(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_JALR,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Load Byte
*
* lb rd, rs1, imm
*
* rd = (8 bit) M[rs1+imm]
*/
static inline int
rv32_lb(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_LB,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Load Byte Unsigned
*
* lbu rd, rs1, imm
*
* rd = (8 bit) M[rs1+imm]
*/
static inline int
rv32_lbu(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_LBU,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* ld
* lh
* lhu
*/
/**
* Load Upper Immediate
*
* lui rd, imm
*
* rd = (32 bit) imm
*/
static inline int
rv32_lui(rv32_prog_t* prog, rv_reg_t rd, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_LUI,
.rd = rd,
.rs1 = REG_NULL,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Load Word
*
* lw rd, rs1, imm
*
* rd = (32 bit) M[rs1+imm]
*/
static inline int
rv32_lw(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_LW,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Load Word Unsigned
*
* lwu rd, rs1, imm
*
* rd = (32 bit) u M[rs1+imm]
*/
// static inline int
// rv32_lwu(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
// return emit_rv32_instr(prog, RV_LWU, rd, rs1, REG_NULL, imm);
// }
/**
* OR
*
* or rd, rs1, rs2
*
* rd = rs1 | rs2
*/
static inline int
rv32_or(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) {
rv32_instr_t instr = {
.instr_type = RV_OR,
.rd = rd,
.rs1 = rs1,
.rs2 = rs2,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* OR Immediate
*
* ori rd, rs1, imm
*
* rd = rs1 | imm (imm maybe cut)
*/
static inline int
rv32_ori(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_ORI,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Store Byte
*
* sb rs2, rs1, imm
*
* M[rs1+imm] = rs2
*/
static inline int
rv32_sb(rv32_prog_t* prog, rv_reg_t rs2, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_SB,
.rd = REG_NULL,
.rs1 = rs1,
.rs2 = rs2,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* sd sh
*/
/**
* Shift Left
*
* sll rd, rs1, rs2
*
* rd = rs1 << rs2
*/
static inline int
rv32_sll(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) {
rv32_instr_t instr = {
.instr_type = RV_SLL,
.rd = rd,
.rs1 = rs1,
.rs2 = rs2,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Shift Left Immediate
*
* slli rd, rs1, imm
*
* rd = rs1 << imm (maybe cut it)
*/
static inline int
rv32_slli(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_SLLI,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Set Less Than
*
* slt rd, rs1, rs2
*
* rd = (rs1 < rs2) ? 1 : 0
*/
static inline int
rv32_slt(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) {
rv32_instr_t instr = {
.instr_type = RV_SLT,
.rd = rd,
.rs1 = rs1,
.rs2 = rs2,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Set Less Than Immediate
*
* slti rd, rs1, imm
*
* rd = (rs1 < imm) ? 1 : 0
*/
static inline int
rv32_slti(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_SLTI,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Set Less Than Immediate Unsigned
*
* sltiu rd, rs1, imm
*
* rd = (u rs1 < imm) ? 1 : 0
*/
static inline int
rv32_sltiu(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_SLTIU,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Set Less Than Unsigned
*
* sltu rd, rs1, rs2
*
* rd = (u rs1 < u rs2) ? 1 : 0
*/
static inline int
rv32_sltu(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) {
rv32_instr_t instr = {
.instr_type = RV_SLTU,
.rd = rd,
.rs1 = rs1,
.rs2 = rs2,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Shift Right Arithmetic
*
* sra rd, rs1, rs2
*
* rd = rs1 >> rs2
*/
static inline int
rv32_sra(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) {
rv32_instr_t instr = {
.instr_type = RV_SRA,
.rd = rd,
.rs1 = rs1,
.rs2 = rs2,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Shift Right Arithmetic Immediate
*
* srai rd, rs1, imm
*
* rd = rs1 >> imm (maybe cut it)
*/
static inline int
rv32_srai(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_SRAI,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Shift Right Logical
*
* srl rd, rs1, rs2
*
* rd = (u rs1) >> rs2
*/
static inline int
rv32_srl(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) {
rv32_instr_t instr = {
.instr_type = RV_SRL,
.rd = rd,
.rs1 = rs1,
.rs2 = rs2,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Shift Right Logical Immediate
*
* srli rd, rs1, imm
*
* rd = (u rs1) >> imm (maybe cut it)
*/
static inline int
rv32_srli(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_SRLI,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* SUBtract
*
* sub rd, rs1, rs2
*
* rd = rs1 - rs2
*/
static inline int
rv32_sub(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) {
rv32_instr_t instr = {
.instr_type = RV_SUB,
.rd = rd,
.rs1 = rs1,
.rs2 = rs2,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* Store Word
*
* sw rs2, rs1, imm
*
* M[rs1+imm] = rs2
*/
static inline int
rv32_sw(rv32_prog_t* prog, rv_reg_t rs2, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_SW,
.rd = REG_NULL,
.rs1 = rs1,
.rs2 = rs2,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* XOR
*
* xor rd, rs1, rs2
*
* rd = rs1 ^ rs2
*/
static inline int
rv32_xor(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, rv_reg_t rs2) {
rv32_instr_t instr = {
.instr_type = RV_XOR,
.rd = rd,
.rs1 = rs1,
.rs2 = rs2,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/**
* XOR Immediate
*
* xori rd, rs1, imm
*
* rd = rs1 ^ imm (maybe cut it)
*/
static inline int
rv32_xori(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1, u32_t imm) {
rv32_instr_t instr = {
.instr_type = RV_XORI,
.rd = rd,
.rs1 = rs1,
.rs2 = REG_NULL,
.imm = imm,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, NULL);
}
/* pseudo instruction */
/**
* branch equal zero
*
* beqz rs1, imm
*
* if (rs1 == 0) PC += imm{alian2}
*/
static inline int
rv32_beqz(rv32_prog_t* prog, rv_reg_t rs1, u32_t imm) {
return rv32_beq(prog, rs1, REG_X0, imm);
}
/**
* branch not equal zero
*
* bnez rs1, imm
*
* if (rs1 != 0) PC += imm{alian2}
*/
static inline int
rv32_bnez(rv32_prog_t* prog, rv_reg_t rs1, u32_t imm) {
return rv32_bne(prog, rs1, REG_X0, imm);
}
/**
* fabs.s fabs.d
* fmv.s fmv.d
* fneg.s fneg.d
*/
/**
* jump
*
* j imm
*
* PC = imm{alian2}
*/
static inline int
rv32_j(rv32_prog_t* prog, u32_t imm) {
// TODO
return rv32_jal(prog, REG_X0, imm);
}
/**
* jump register
*
* jr rs1
*
* PC = rs1
*/
static inline int
rv32_jr(rv32_prog_t* prog, rv_reg_t rs1) {
// TODO
return rv32_jalr(prog, REG_X0, rs1, 0);
}
/**
* load address
*
* la rd
*
* rd = address
*/
static inline int
rv32_la(rv32_prog_t* prog, rv_reg_t rd, u32_t imm) {
// TODO
return rv32_auipc(prog, rd, imm);
}
/**
* load immediate
*
* li rd, imm
*
* rd = imm
*/
static inline int
rv32_li(rv32_prog_t* prog, rv_reg_t rd, i32_t imm) {
if (imm >= -2048 && imm <= 2047) {
return rv32_addi(prog, rd, REG_X0, imm);
} else {
int ret = rv32_lui(prog, rd, imm);
rv32_addi(prog, rd, rd, imm);
return ret;
}
}
/**
* move
*
* mv rd, rs1
*
* rd = rs1
*/
static inline int
rv32_mv(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1) {
return rv32_addi(prog, rd, rs1, 0);
}
/**
* negate
*
* neg rd, rs1
*
* rd = -rs1
*/
static inline int
rv32_neg(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1) {
return rv32_sub(prog, rd, REG_X0, rs1);
}
/**
* no opreation
*
* nop
*
* x0 = x0
*/
static inline int
rv32_nop(rv32_prog_t* prog) {
return rv32_addi(prog, REG_X0, REG_X0, 0);
}
/**
* not
*
* not rd, rs1
*
* rd = ~rs1
*/
static inline int
rv32_not(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1) {
return rv32_xori(prog, rd, rs1, ~0);
}
/**
* return
*
* ret
*
* PC = ra
*/
static inline int
rv32_ret(rv32_prog_t* prog) {
return rv32_jalr(prog, REG_X0, REG_RA, 0);
}
/**
* set equal zero
*
* seqz rd, rs1
*
* rd = (rs1 == 0) ? 1 : 0
*/
static inline int
rv32_seqz(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1) {
// TODO
return rv32_sltiu(prog, rd, rs1, 1);
}
/**
* set not equal zero
*
* snez rd, rs1
*
* rd = (rs1 != 0) ? 1 : 0
*/
static inline int
rv32_snez(rv32_prog_t* prog, rv_reg_t rd, rv_reg_t rs1) {
// TODO
return rv32_sltu(prog, rd, rs1, REG_X0);
}
/* instruction of rotated (using label) */
static inline int
rv32_bne_l(rv32_prog_t* prog, rv_reg_t rs1, rv_reg_t rs2, symasm_entry_t* label) {
rv32_instr_t instr = {
.instr_type = RV_BNE,
.rd = REG_NULL,
.rs1 = rs1,
.rs2 = rs2,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, label);
}
static inline int
rv32_jal_l(rv32_prog_t* prog, rv_reg_t rd, symasm_entry_t* label) {
rv32_instr_t instr = {
.instr_type = RV_JAL,
.rd = rd,
.rs1 = REG_NULL,
.rs2 = REG_NULL,
.imm = 0,
};
return emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, label);
}
static inline int
rv32_call_l(rv32_prog_t* prog, symasm_entry_t* label) {
rv32_instr_t instr;
instr.instr_type = RV_JAL;
instr.rd = REG_NULL;
instr.rs1 = REG_NULL;
instr.rs2 = REG_NULL;
instr.imm = 0;
int ret = rv32_auipc(prog, REG_RA, REG_X0);
instr.instr_type = RV_JALR;
instr.rd = REG_RA;
instr.rs1 = REG_RA;
instr.rs2 = REG_NULL;
instr.imm = 4;
emit_rv32_instr(prog, &instr, EMIT_PUSH_BACK, label);
return ret;
}
/* append label */
static inline int
rv32_append_label(rv32_prog_t* prog, symasm_entry_t* label, u32_t offset) {
// prog->symtab
symtab_asm_put(&prog->symtab, label, offset);
return 0;
}
#endif

View File

@ -0,0 +1,40 @@
#include "symtab_asm.h"
static u32_t hash_func(const symasm_entry_t* key) {
return rt_strhash(key->name);
}
static int cmp_func(const symasm_entry_t* k1, const symasm_entry_t* k2) {
return rt_strcmp(k1->name, k2->name);
}
void init_symtab_asm(symtab_asm_t* symtab) {
init_hashtable(&symtab->symtab);
symtab->symtab.hash_func = (u32_t(*)(const void*))hash_func;
symtab->symtab.key_cmp = (int(*)(const void*, const void*))cmp_func;
}
void symtab_asm_put(symtab_asm_t* symtab, symasm_entry_t* _entry, u32_t address) {
// FIXME maybe memory leak
u32_t* addr = salloc_alloc(sizeof(u32_t));
if (addr == NULL) {
LOG_FATAL("salloc_alloc failure");
}
symasm_entry_t* entry = salloc_alloc(sizeof(symasm_entry_t));
if (entry == NULL) LOG_FATAL("malloc failure");
*entry = *_entry;
*addr = address;
void* ret = hashtable_set(&symtab->symtab, entry, addr);
if (ret != NULL) {
LOG_ERROR("Symbol %s already exists", entry->name);
}
}
u32_t* symtab_asm_get(symtab_asm_t* symtab, symasm_entry_t* entry) {
u32_t* addr = hashtable_get(&symtab->symtab, entry);
return addr;
}
void symtab_asm_destroy(symtab_asm_t* symtab) {
hashtable_destory(&symtab->symtab);
}

View File

@ -0,0 +1,26 @@
#ifndef __SMCC_SYMTAB_ASM_H__
#define __SMCC_SYMTAB_ASM_H__
#include <lib/core.h>
#include <lib/utils/ds/hashtable.h>
typedef enum symasm_attr {
GLOBAL,
LOCAL,
} symasm_attr_t;
typedef struct symasm_entry {
const char* name;
symasm_attr_t attr;
} symasm_entry_t;
typedef struct symtab_asm {
hash_table_t symtab;
} symtab_asm_t;
void init_symtab_asm(symtab_asm_t* symtab);
void symtab_asm_put(symtab_asm_t* symtab, symasm_entry_t* entry, u32_t address);
u32_t* symtab_asm_get(symtab_asm_t* symtab, symasm_entry_t* entry);
void symtab_asm_destroy(symtab_asm_t* symtab);
#endif

57
ccompiler/Makefile Normal file
View File

@ -0,0 +1,57 @@
# all: cc
# # run: ccompiler
# # ./ccompiler test.c flat.bin
# # simple_test:
# # make -C tests/simple
# cc: frontend middleend backend ccompiler.c test_main.c
# gcc -g ccompiler.c test_main.c -I../ -L./frontend -lfrontend -L./middleend -lmiddleend -L./backend -lbackend -L../lib -lcore -o cc
# frontend:
# make -C ./frontend
# middleend:
# make -C ./middleend
# backend:
# make -C ./backend
# clean:
# rm -f cc
# make -C ./frontend clean
# make -C ./middleend clean
# make -C ./backend clean
# 顶层Makefile修改
CC = gcc
AR = ar
CFLAGS = -g -Wall -I..
MODULES = frontend middleend backend
FRONTEND_SUBDIRS = lexer parser parser/ast
MODULES += $(addprefix frontend/, $(FRONTEND_SUBDIRS))
MIDDLEEND_MODULES = ir
MODULES += middleend/$(MIDDLEEND_MODULES)
BACKEND_MODULES = riscv32
MODULES += backend/$(BACKEND_MODULES)
# 自动收集所有子模块源文件
EXCLUDE = test*.c
SRCS = $(filter-out $(EXCLUDE), $(wildcard $(addsuffix /*.c,$(MODULES))))
SRCS += ccompiler.c
OBJS = $(SRCS:.c=.o)
libcc.a: $(OBJS)
$(AR) rcs $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f libcc.a $(OBJS)

View File

@ -0,0 +1,30 @@
# 编译器设置
CC = gcc
AR = ar
CFLAGS = -g -Wall -I../..
RISCV32_DIR = ./riscv32
# 源文件列表
SRCS = \
backend.c \
$(RISCV32_DIR)/riscv32.c
# 生成目标文件列表
OBJS = $(SRCS:.c=.o)
# 最终目标
TARGET = libbackend.a
all: $(TARGET)
$(TARGET): $(OBJS)
$(AR) rcs $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f $(OBJS) $(TARGET)
.PHONY: all clean

View File

@ -0,0 +1,23 @@
#include "backend.h"
int gen_asm_from_ir(ir_prog_t* ir, cc_arch_t arch, asm_prog_t* out_asm) {
switch (arch) {
case CC_ARCH_RISCV32:
// TODO using maroc to choice
init_rv32_prog(&(out_asm->rv32), NULL);
gen_rv32_from_ir(ir, &(out_asm->rv32));
break;
case CC_ARCH_X86_32:
default:
Panic("Unsupported arch");
break;
}
return 0;
}
asm_prog_t* cc_backend(ir_prog_t* ir, cc_backend_conf_t* conf) {
// TODO
asm_prog_t* bin = (asm_prog_t*)salloc_alloc(sizeof(asm_prog_t));
gen_asm_from_ir(ir, conf->arch, bin);
return bin;
}

View File

@ -0,0 +1,30 @@
#ifndef __SMCC_CC_BACKEND_H__
#define __SMCC_CC_BACKEND_H__
// TODO Use Maroc to choice architecture
#ifndef __SMCC_CC_NO_RISCV32__
#include "riscv32/riscv32.h"
#endif
// #ifndef __SMCC_CC_NO_X86_32__
// #include "x86_32/x86_32.h"
// #endif
// TODO 统一 汇编器 接口
#include <assembler/assembler.h>
#include "../middleend/ir/ir.h"
typedef enum cc_arch {
CC_ARCH_RISCV32,
CC_ARCH_X86_32
} cc_arch_t;
typedef union asm_prog asm_prog_t;
int gen_asm_from_ir(ir_prog_t* ir, cc_arch_t arch, asm_prog_t* asm_prog);
typedef struct cc_backend_conf {
cc_arch_t arch;
} cc_backend_conf_t;
asm_prog_t* cc_backend(ir_prog_t* ir, cc_backend_conf_t* conf);
#endif

View File

@ -1,21 +0,0 @@
all: ccompiler
run: ccompiler
./ccompiler test.c flat.bin
simple_test:
make -C tests/simple
ccompiler: frontend ir
gcc -g rv32.c -I../../.. -L../../frontend -lfrontend -L../../middleend -lmiddleend -L../../../lib -lcore -o ccompiler
frontend:
make -C ../../frontend
ir:
make -C ../../middleend
clean:
rm -f ccompiler flat.bin
make -C ../../frontend clean
make -C ../../middleend clean

View File

@ -0,0 +1,41 @@
# 后端代码生成
## riscv32i
> 仿照ripes的syscall实现了rv32-vm
### syscall ecall 系统调用
```c
// ecall 系统调用函数实现
#define ECALL_PNT_INT(num) \
ADDI(REG_A0, REG_X0, num), \
ADDI(REG_A7, REG_X0, 0x1), \
ECALL(),
#define ECALL_PNT_STR(str) \
ADDI(REG_A0, REG_X0, str), \
ADDI(REG_A7, REG_X0, 0x4), \
ECALL(),
#define ECALL_EXIT(errno) \
ADDI(REG_A0, REG_X0, errno), \
ADDI(REG_A7, REG_X0, 10), \
ECALL(),
#define ECALL_SCAN_INT(int) \
ADDI(REG_A7, (1025 + 4)), \
ECALL(),
#define ECALL_SCAN_STR(str) \
ADDI(REG_A0, REG_X0, str), \
ADDI(REG_A7, REG_X0, (1025 + 5)), \
ECALL(),
// 函数声明
void ecall_pnt_int(int num);
void ecall_pnt_str(char *str);
void ecall_exit(int errno);
int ecall_scani();
void ecall_scans(char *str);
```

View File

@ -0,0 +1,301 @@
#include "riscv32.h"
#include <assembler/riscv32/riscv32_instr.h>
typedef struct {
ir_func_t* func;
int stack_offset;
int stack_base;
int func_idx;
int block_idx;
} gen_ctx_t;
static inline int stack_pos(ir_node_t* ptr, gen_ctx_t *ctx) {
// ir_func_t *func, int stack_base, int stack_offset
int offset = ctx->stack_base;
for (int i = 0; i < ctx->func->bblocks.size; i ++) {
ir_bblock_t* block = vector_at(ctx->func->bblocks, i);
for (int i = 0; i < block->instrs.size; i++) {
if (vector_at(block->instrs, i) == ptr) {
offset += i * 4;
Assert(offset >= 0 && offset < ctx->stack_offset);
return offset;
}
}
offset += block->instrs.size * 4;
}
Panic("stack pos got error");
return 0;
}
static int system_func(const char* name) {
static struct {
const char* name;
int ecall_num;
} defined_func[] = {
{"ecall_pnt_int", 1},
{"ecall_pnt_char", 11},
{"ecall_scan_int", 1025 + 4},
};
for (int i = 0; i < sizeof(defined_func)/sizeof(defined_func[0]); i++) {
if (rt_strcmp(name, defined_func[i].name) == 0) {
return defined_func[i].ecall_num;
}
}
return -1;
}
static int get_node_val(rv32_prog_t* out_asm, gen_ctx_t* ctx, ir_node_t* ptr, int reg) {
int len = 0;
switch (ptr->tag) {
case IR_NODE_CONST_INT: {
// TODO
rv32_li(out_asm, reg, ptr->data.const_int.val);
// emit_rv32_instr(out_asm, RV_ADDI, reg, reg, 0, ptr->data.const_int.val);
break;
}
default: {
int offset = stack_pos(ptr, ctx);
rv32_lw(out_asm, reg, REG_SP, offset);
break;
}
}
return len;
}
static int gen_instr(rv32_prog_t* out_asm, gen_ctx_t* ctx, ir_node_t* instr) {
int idx = 0;
int offset;
char buf[1024];
symasm_entry_t label;
switch (instr->tag) {
case IR_NODE_ALLOC: {
// TODO
break;
}
case IR_NODE_LOAD: {
offset = stack_pos(instr->data.load.target, ctx);
// t0 = M[sp + offset]
rv32_lw(out_asm, REG_T0, REG_SP, offset);
break;
}
case IR_NODE_STORE: {
idx += get_node_val(out_asm, ctx, instr->data.store.value, REG_T0);
offset = stack_pos(instr->data.store.target, ctx);
// M[sp + offset] = t0
rv32_sw(out_asm, REG_T0, REG_SP, offset);
break;
}
case IR_NODE_RET: {
// A0 = S0
if (instr->data.ret.ret_val != NULL) {
idx += get_node_val(out_asm, ctx, instr->data.ret.ret_val, REG_A0);
}
// ra = M[sp + 0]
rv32_lw(out_asm, REG_RA, REG_SP, 0);
// sp = sp + stack_offset
rv32_addi(out_asm, REG_SP, REG_SP, ctx->stack_offset);
// ret == JALR(REG_X0, REG_RA, 0)
rv32_ret(out_asm);
break;
}
case IR_NODE_OP: {
idx += get_node_val(out_asm, ctx, instr->data.op.lhs, REG_T1);
idx += get_node_val(out_asm, ctx, instr->data.op.rhs, REG_T2);
rv32_instr_t _instr = {
.rd = REG_T0,
.rs1 = REG_T1,
.rs2 = REG_T2,
.imm = 0
};
#define GEN_BIN_OP(type) _instr.instr_type = type, \
emit_rv32_instr(out_asm, &_instr, EMIT_PUSH_BACK, NULL)
switch (instr->data.op.op) {
case IR_OP_ADD:
GEN_BIN_OP(RV_ADD);
break;
case IR_OP_SUB:
GEN_BIN_OP(RV_SUB);
break;
case IR_OP_MUL:
GEN_BIN_OP(RV_MUL);
break;
case IR_OP_DIV:
GEN_BIN_OP(RV_DIV);
break;
case IR_OP_MOD:
GEN_BIN_OP(RV_REM);
break;
case IR_OP_EQ:
GEN_BIN_OP(RV_XOR);
rv32_seqz(out_asm, REG_T0, REG_T0);
break;
case IR_OP_GE:
GEN_BIN_OP(RV_SLT);
rv32_seqz(out_asm, REG_T0, REG_T0);
break;
case IR_OP_GT:
// SGT(rd, rs1, rs2) SLT(rd, rs2, rs1)
// GENCODE(SGT(REG_T0, REG_T1, REG_T2));
rv32_slt(out_asm, REG_T0, REG_T2, REG_T1);
break;
case IR_OP_LE:
// GENCODE(SGT(REG_T0, REG_T1, REG_T2));
rv32_slt(out_asm, REG_T0, REG_T2, REG_T1);
rv32_seqz(out_asm, REG_T0, REG_T0);
break;
case IR_OP_LT:
rv32_slt(out_asm, REG_T0, REG_T1, REG_T2);
break;
case IR_OP_NEQ:
GEN_BIN_OP(RV_XOR);
break;
default:
LOG_ERROR("ERROR gen_instr op in riscv");
break;
}
offset = stack_pos(instr, ctx);
rv32_sw(out_asm, REG_T0, REG_SP, offset);
break;
}
case IR_NODE_BRANCH: {
get_node_val(out_asm, ctx, instr->data.branch.cond, REG_T0);
rt.snprintf(buf, sizeof(buf), "L%s%p", instr->data.branch.true_bblock->label, instr->data.branch.true_bblock);
label.name = strpool_intern(out_asm->strpool, buf);
label.attr = LOCAL;
rv32_bne_l(out_asm, REG_T0, REG_X0, &label);
rt.snprintf(buf, sizeof(buf), "L%s%p", instr->data.branch.false_bblock->label, instr->data.branch.false_bblock);
label.name = strpool_intern(out_asm->strpool, buf);
label.attr = LOCAL;
rv32_jal_l(out_asm, REG_X0, &label);
break;
}
case IR_NODE_JUMP: {
// TODO
rt.snprintf(buf, sizeof(buf), "L%s%p", instr->data.jump.target_bblock->label, instr->data.jump.target_bblock);
label.name = strpool_intern(out_asm->strpool, buf);
label.attr = LOCAL;
rv32_jal_l(out_asm, REG_X0, &label);
break;
}
case IR_NODE_CALL: {
if (instr->data.call.args.size > 8) {
LOG_ERROR("can't add so much params");
}
int param_regs[8] = {
REG_A0, REG_A1, REG_A2, REG_A3,
REG_A4, REG_A5, REG_A6, REG_A7
};
for (int i = 0; i < instr->data.call.args.size; i++) {
ir_node_t* param = vector_at(instr->data.call.args, i);
idx += get_node_val(out_asm, ctx, param, param_regs[i]);
}
int system_func_idx = system_func(instr->data.call.callee->name);
if (system_func_idx != -1) {
rv32_li(out_asm, REG_A7, system_func_idx);
rv32_ecall(out_asm);
goto CALL_END;
}
/*
// GENCODES(CALL(0));
// AUIPC(REG_X1, REG_X0), \
// JALR(REG_X1, REG_X1, offset)
*/
// TODO CALL
label.name = strpool_intern(out_asm->strpool, instr->data.call.callee->name);
label.attr = GLOBAL;
rv32_call_l(out_asm, &label);
CALL_END:
offset = stack_pos(instr, ctx);
rv32_sw(out_asm, REG_A0, REG_SP, offset);
break;
}
default:
LOG_ERROR("ERROR gen_instr in riscv");
}
return idx;
}
static int gen_block(rv32_prog_t* out_asm, gen_ctx_t* ctx, ir_bblock_t* block) {
symasm_entry_t label;
char buf[1024];
rt.snprintf(buf, sizeof(buf), "L%s%p", block->label, block);
label.name = strpool_intern(out_asm->strpool, buf);
label.attr = LOCAL;
rv32_append_label(out_asm, &label, out_asm->text.size);
for (int i = 0; i < block->instrs.size; i ++) {
gen_instr(out_asm, ctx, vector_at(block->instrs, i));
}
return 0;
}
static int gen_func(rv32_prog_t* out_asm, ir_func_t* func) {
gen_ctx_t ctx;
symasm_entry_t label = {
.name = strpool_intern(out_asm->strpool, func->name),
.attr = GLOBAL,
};
rv32_append_label(out_asm, &label, out_asm->text.size);
int stack_base = 4;
int stack_offset = stack_base;
for (int i = 0; i < func->bblocks.size; i++) {
// TODO every instr push ret val to stack
stack_offset += 4 * (*vector_at(func->bblocks, i)).instrs.size;
}
ctx.func = func;
ctx.stack_base = stack_base;
ctx.stack_offset = stack_offset;
ctx.func_idx = 0;
ctx.block_idx = 0;
// TODO Alignment by 16
// sp = sp - stack_offset;
rv32_addi(out_asm, REG_SP, REG_SP, -stack_offset);
// M[sp] = ra;
rv32_sw(out_asm, REG_RA, REG_SP, 0);
int param_regs[8] = {
REG_A0, REG_A1, REG_A2, REG_A3,
REG_A4, REG_A5, REG_A6, REG_A7
};
if (func->params.size > 8) {
LOG_ERROR("can't add so much params");
}
for (int i = 0; i < func->params.size; i++) {
int offset = stack_pos(vector_at(func->params, i), &ctx);
// M[sp + offset] = param[idx];
rv32_sw(out_asm, param_regs[i], REG_SP, offset);
}
for(int i = 0; i < func->bblocks.size; i ++) {
gen_block(out_asm, &ctx ,vector_at(func->bblocks, i));
}
return 0;
}
int gen_rv32_from_ir(ir_prog_t* ir, rv32_prog_t* out_asm) {
init_rv32_prog(out_asm, NULL);
for(int i = 0; i < ir->funcs.size; i ++) {
gen_func(out_asm, vector_at(ir->funcs, i));
}
return 0;
// // Got Main pos;
// for (int i = 0; i < prog->funcs.size; i++) {
// if (strcmp(vector_at(prog->funcs, i)->name, "main") == 0) {
// return jmp_cache[i];
// }
// }
// LOG_ERROR("main not found");
}

View File

@ -0,0 +1,10 @@
#ifndef __SMCC_CC_RISCV32_H__
#define __SMCC_CC_RISCV32_H__
#include <assembler/assembler.h>
#include <assembler/riscv32/riscv32.h>
#include "../../middleend/ir/ir.h"
int gen_rv32_from_ir(ir_prog_t* ir, rv32_prog_t* out_asm);
#endif

View File

@ -1,464 +0,0 @@
#define RISCV_VM_BUILDIN_ECALL
#include "rv32gen.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
// 指令编码联合体(自动处理小端序)
typedef union rv32code {
uint32_t code;
uint8_t bytes[4];
} rv32code_t;
#include "../../frontend/frontend.h"
#include "../../middleend/middleend.h"
typedef struct {
int code_pos;
int to_idx;
int cur_idx;
int base_offset;
enum {
JMP_BRANCH,
JMP_JUMP,
JMP_CALL,
} type;
} jmp_t;
static struct {
vector_header(codes, rv32code_t);
int stack_offset;
int stack_base;
int tmp_reg;
ir_bblock_t* cur_block;
ir_func_t* cur_func;
ir_prog_t* prog;
vector_header(jmp, jmp_t*);
vector_header(call, jmp_t*);
int cur_func_offset;
int cur_block_offset;
} ctx;
int write_inst(union rv32code ins, FILE* fp) {
return fwrite(&ins, sizeof(union rv32code), 1, fp);
}
#define GENCODE(code) vector_push(ctx.codes, (rv32code_t)(code)); len += 4
#define GENCODES(...) do { \
rv32code_t codes[] = { \
__VA_ARGS__ \
}; \
for (int i = 0; i < sizeof(codes) / sizeof(codes[0]); i ++) { \
GENCODE(codes[i]); \
} \
} while (0)
static int stack_offset(ir_node_t* ptr) {
int offset = ctx.stack_base;
for (int i = 0; i < ctx.cur_func->bblocks.size; i ++) {
ir_bblock_t* block = vector_at(ctx.cur_func->bblocks, i);
for (int i = 0; i < block->instrs.size; i++) {
if (vector_at(block->instrs, i) == ptr) {
offset += i * 4;
assert(offset >= 0 && offset < ctx.stack_offset);
return offset;
}
}
offset += block->instrs.size * 4;
}
assert(0);
}
static int block_idx(ir_bblock_t* toblock) {
for (int i = 0; i < ctx.cur_func->bblocks.size; i ++) {
ir_bblock_t* block = vector_at(ctx.cur_func->bblocks, i);
if (toblock == block) {
return i;
}
}
assert(0);
}
static int func_idx(ir_func_t* tofunc) {
for (int i = 0; i < ctx.prog->funcs.size; i ++) {
ir_func_t* func = vector_at(ctx.prog->funcs, i);
if (tofunc == func) {
return i;
}
}
assert(0);
}
static int system_func(const char* name) {
static struct {
const char* name;
int ecall_num;
} defined_func[] = {
{"ecall_pnt_int", 1},
{"ecall_pnt_char", 11},
{"ecall_scan_int", 1025 + 4},
};
for (int i = 0; i < sizeof(defined_func)/sizeof(defined_func[0]); i++) {
if (strcmp(name, defined_func[i].name) == 0) {
return defined_func[i].ecall_num;
}
}
return -1;
}
static int get_node_val(ir_node_t* ptr, int reg) {
int len = 0;
switch (ptr->tag) {
case IR_NODE_CONST_INT: {
GENCODES(LI(reg, ptr->data.const_int.val));
break;
}
// case IR_NODE_CALL: {
// // GENCODE(SW(REG_A0, REG_SP, ctx.stack_offset));
// // GENCODE()
// // break;
// }
default: {
int offset = stack_offset(ptr);
GENCODE(LW(reg, REG_SP, offset));
break;
}
}
return len;
}
static int gen_instr(ir_bblock_t* block, ir_node_t* instr) {
int len = 0;
int offset;
switch (instr->tag) {
case IR_NODE_ALLOC: {
break;
}
case IR_NODE_LOAD: {
// S1 = *(S0 + imm)
offset = stack_offset(instr->data.load.target);
GENCODE(LW(REG_T0, REG_SP, offset));
break;
}
case IR_NODE_STORE: {
// *(S0 + imm) = S1
len += get_node_val(instr->data.store.value, REG_T0);
offset = stack_offset(instr->data.store.target);
GENCODE(SW(REG_T0, REG_SP, offset));
break;
}
case IR_NODE_RET: {
// A0 = S0
if (instr->data.ret.ret_val != NULL) {
len += get_node_val(instr->data.ret.ret_val, REG_A0);
}
GENCODE(LW(REG_RA, REG_SP, 0));
GENCODE(ADDI(REG_SP, REG_SP, ctx.stack_offset));
GENCODE(RET());
break;
}
case IR_NODE_OP: {
len += get_node_val(instr->data.op.lhs, REG_T1);
len += get_node_val(instr->data.op.rhs, REG_T2);
switch (instr->data.op.op) {
case IR_OP_ADD:
GENCODE(ADD(REG_T0, REG_T1, REG_T2));
break;
case IR_OP_SUB:
GENCODE(SUB(REG_T0, REG_T1, REG_T2));
break;
case IR_OP_MUL:
GENCODE(MUL(REG_T0, REG_T1, REG_T2));
break;
case IR_OP_DIV:
GENCODE(DIV(REG_T0, REG_T1, REG_T2));
break;
case IR_OP_MOD:
GENCODE(REM(REG_T0, REG_T1, REG_T2));
break;
case IR_OP_EQ:
GENCODE(XOR(REG_T0, REG_T1, REG_T2));
GENCODE(SEQZ(REG_T0, REG_T0));
break;
case IR_OP_GE:
GENCODE(SLT(REG_T0, REG_T1, REG_T2));
GENCODE(SEQZ(REG_T0, REG_T0));
break;
case IR_OP_GT:
GENCODE(SGT(REG_T0, REG_T1, REG_T2));
break;
case IR_OP_LE:
GENCODE(SGT(REG_T0, REG_T1, REG_T2));
GENCODE(SEQZ(REG_T0, REG_T0));
break;
case IR_OP_LT:
GENCODE(SLT(REG_T0, REG_T1, REG_T2));
break;
case IR_OP_NEQ:
GENCODE(XOR(REG_T0, REG_T1, REG_T2));
break;
default:
LOG_ERROR("ERROR gen_instr op in riscv");
break;
}
offset = stack_offset(instr);
GENCODE(SW(REG_T0, REG_SP, offset));
break;
}
case IR_NODE_BRANCH: {
len += get_node_val(instr->data.branch.cond, REG_T0);
int tidx = block_idx(instr->data.branch.true_bblock);
int fidx = block_idx(instr->data.branch.false_bblock);
int cidx = block_idx(ctx.cur_block);
jmp_t* jmp;
jmp = rt._malloc(sizeof(jmp_t));
*jmp = (jmp_t) {
.base_offset = 8,
.code_pos = ctx.codes.size,
.type = JMP_BRANCH,
.to_idx = tidx,
.cur_idx=cidx,
};
vector_push(ctx.jmp, jmp);
GENCODE(BNEZ(REG_T0, 0));
jmp = rt._malloc(sizeof(jmp_t));
*jmp = (jmp_t) {
.base_offset = 4,
.code_pos = ctx.codes.size,
.type = JMP_JUMP,
.to_idx = fidx,
.cur_idx=cidx,
};
vector_push(ctx.jmp, jmp);
GENCODE(J(0));
break;
}
case IR_NODE_JUMP: {
int idx = block_idx(instr->data.jump.target_bblock);
jmp_t* jmp = rt._malloc(sizeof(jmp_t));
*jmp = (jmp_t) {
.base_offset = 4,
.code_pos = ctx.codes.size,
.type = JMP_JUMP,
.to_idx = idx,
.cur_idx=block_idx(ctx.cur_block),
};
vector_push(ctx.jmp, jmp);
GENCODE(J(0));
break;
}
case IR_NODE_CALL: {
if (instr->data.call.args.size > 8) {
LOG_ERROR("can't add so much params");
}
int param_regs[8] = {
REG_A0, REG_A1, REG_A2, REG_A3,
REG_A4, REG_A5, REG_A6, REG_A7
};
for (int i = 0; i < instr->data.call.args.size; i++) {
ir_node_t* param = vector_at(instr->data.call.args, i);
len += get_node_val(param, param_regs[i]);
}
int system_func_idx = system_func(instr->data.call.callee->name);
if (system_func_idx != -1) {
// ecall
GENCODES(
ADDI(REG_A7, REG_X0, system_func_idx),
ECALL()
);
goto CALL_END;
}
jmp_t* jmp = rt._malloc(sizeof(jmp_t));
*jmp = (jmp_t) {
.base_offset = ctx.cur_func_offset + ctx.cur_block_offset + len,
.code_pos = ctx.codes.size,
.type = JMP_CALL,
.to_idx = func_idx(instr->data.call.callee),
.cur_idx = func_idx(ctx.cur_func),
};
vector_push(ctx.call, jmp);
GENCODES(CALL(0));
CALL_END:
offset = stack_offset(instr);
GENCODE(SW(REG_A0, REG_SP, offset));
break;
}
default:
LOG_ERROR("ERROR gen_instr in riscv");
}
return len;
}
static int gen_block(ir_bblock_t* block) {
int len = 0;
ctx.cur_block = block;
for (int i = 0; i < block->instrs.size; i ++) {
ctx.cur_block_offset = len;
len += gen_instr(block, vector_at(block->instrs, i));
}
return len;
}
static int gen_func(ir_func_t* func) {
int len = 0;
ctx.cur_func = func;
ctx.stack_base = 16;
ctx.stack_offset = ctx.stack_base;
for (int i = 0; i < func->bblocks.size; i++) {
ctx.stack_offset += 4 * (*vector_at(func->bblocks, i)).instrs.size;
}
GENCODE(ADDI(REG_SP, REG_SP, -ctx.stack_offset));
GENCODE(SW(REG_RA, REG_SP, 0));
int param_regs[8] = {
REG_A0, REG_A1, REG_A2, REG_A3,
REG_A4, REG_A5, REG_A6, REG_A7
};
if (func->params.size > 8) {
LOG_ERROR("can't add so much params");
}
for (int i = 0; i < func->params.size; i++) {
int offset = stack_offset(vector_at(func->params, i));
GENCODE(SW(param_regs[i], REG_SP, offset));
}
int jmp_cache[func->bblocks.size + 1];
if (ctx.jmp.data != NULL) vector_free(ctx.jmp);
vector_init(ctx.jmp);
jmp_cache[0] = 0;
for(int i = 0; i < func->bblocks.size; i ++) {
ctx.cur_func_offset = len;
jmp_cache[i + 1] = jmp_cache[i];
int ret = gen_block(vector_at(func->bblocks, i));
jmp_cache[i + 1] += ret;
len += ret;
}
for (int i = 0; i < ctx.jmp.size; i++) {
jmp_t* jmp = vector_at(ctx.jmp, i);
int32_t code = 0;
int offset = jmp_cache[jmp->to_idx] - (jmp_cache[jmp->cur_idx + 1] - jmp->base_offset);
if (jmp->type == JMP_JUMP) {
code = J(offset);
} else {
code = BNEZ(REG_T0, offset);
}
ctx.codes.data[jmp->code_pos] = (rv32code_t) {
.code = code,
};
}
return len;
}
static int gen_code(ir_prog_t* prog) {
ctx.prog = prog;
for (int i = 0; i < prog->extern_funcs.size; i++) {
if (system_func(prog->extern_funcs.data[i]->name) == -1) {
LOG_ERROR("func %s not defined and not a system func", prog->extern_funcs.data[i]->name);
}
}
int len = 0;
int jmp_cache[prog->funcs.size + 1];
for(int i = 0; i < prog->funcs.size; i ++) {
jmp_cache[i + 1] = jmp_cache[i];
int ret = gen_func(vector_at(prog->funcs, i));
jmp_cache[i + 1] += ret;
len += ret;
}
for (int i = 0; i < ctx.call.size; i++) {
jmp_t* jmp = vector_at(ctx.call, i);
int32_t code = 0;
// FIXME ERROR
int offset = jmp_cache[jmp->to_idx] - (jmp_cache[jmp->cur_idx] + jmp->base_offset);
assert(offset > -0xfff && offset < 0xfff);
int32_t codes[2] = {
CALL(offset)
};
for (int i = 0; i < 2; i++) {
ctx.codes.data[jmp->code_pos + i] = (rv32code_t) {
.code = codes[i],
};
}
}
// Got Main pos;
for (int i = 0; i < prog->funcs.size; i++) {
if (strcmp(vector_at(prog->funcs, i)->name, "main") == 0) {
return jmp_cache[i];
}
}
LOG_ERROR("main not found");
}
int main(int argc, char** argv) {
// gcc rv32ima_codegen.c -o rv32gen.exe
init_lib_core();
log_set_level(NULL, LOG_LEVEL_NOTSET);
const char* infilename = "test.c";
const char* outfilename = "flat.bin";
if (argc >= 2) {
infilename = argv[1];
}
if (argc >= 3) {
outfilename = argv[2];
}
FILE* in = fopen(infilename, "r");
FILE* out = fopen(outfilename, "wb");
if (in == NULL || out == NULL) {
printf("Failed to open file\n");
return 1;
}
ast_node_t* root = frontend(infilename, in, (sread_fn)fread_s);
ir_prog_t* prog = gen_ir_from_ast(root);
int main_pos = gen_code(prog);
#define CRT_CODE_SIZE 16
rv32code_t gcodes[] = {
LI(REG_SP, 0x1000),
LI(REG_RA, 0x0),
CALL(0),
// Exit
ECALL_EXIT2(),
};
main_pos += (CRT_CODE_SIZE - 4) * 4;
assert(main_pos > -0xfff && main_pos < 0xfff);
rv32code_t call_main[2] = {
CALL(main_pos)
};
gcodes[4] = call_main[0];
gcodes[5] = call_main[1];
for (int i = 0; i < CRT_CODE_SIZE; i++) {
write_inst((union rv32code) {
.code = NOP(),
}, out);
}
fflush(out);
assert(CRT_CODE_SIZE >= sizeof(gcodes) / sizeof(gcodes[0]));
fseek(out, 0, SEEK_SET);
fwrite(gcodes, sizeof(gcodes), 1, out);
fflush(out);
fseek(out, CRT_CODE_SIZE * 4, SEEK_SET);
fwrite(ctx.codes.data, sizeof(ctx.codes.data[0]), ctx.codes.size, out);
fflush(out);
fclose(in);
fclose(out);
// printf("comiler end out: %s\n", outfilename);
return 0;
}

View File

@ -1,341 +0,0 @@
#ifndef __RV32I_GEN_H__
#define __RV32I_GEN_H__
/**
31 25 24 20 19 15 14 12 11 7 6 0
imm[31:12] rd 0110111 U lui
imm[31:12] rd 0010111 U auipc
imm[20|10:1|11|19:12] rd 1101111 J jal
imm[11:0] rs1 000 rd 1100111 I jalr
imm[12|10:5] rs2 rs1 000 imm[4:1|11] 1100011 B beq
imm[12|10:5] rs2 rs1 001 imm[4:1|11] 1100011 B bne
imm[12|10:5] rs2 rs1 100 imm[4:1|11] 1100011 B blt
imm[12|10:5] rs2 rs1 101 imm[4:1|11] 1100011 B bge
imm[12|10:5] rs2 rs1 110 imm[4:1|11] 1100011 B bltu
imm[12|10:5] rs2 rs1 111 imm[4:1|11] 1100011 B bgeu
imm[11:0] rs1 000 rd 0000011 I lb
imm[11:0] rs1 001 rd 0000011 I lh
imm[11:0] rs1 010 rd 0000011 I lw
imm[11:0] rs1 100 rd 0000011 I lbu
imm[11:0] rs1 101 rd 0000011 I lhu
imm[11:5] rs2 rs1 000 imm[4:0] 0100011 S sb
imm[11:5] rs2 rs1 001 imm[4:0] 0100011 S sh
imm[11:5] rs2 rs1 010 imm[4:0] 0100011 S sw
imm[11:0] rs1 000 rd 0010011 I addi
imm[11:0] rs1 010 rd 0010011 I slti
imm[11:0] rs1 011 rd 0010011 I sltiu
imm[11:0] rs1 100 rd 0010011 I xori
imm[11:0] rs1 110 rd 0010011 I ori
imm[11:0] rs1 111 rd 0010011 I andi
0000000 shamt rs1 001 rd 0010011 I slli
0000000 shamt rs1 101 rd 0010011 I srli
0100000 shamt rs1 101 rd 0010011 I srai
0000000 rs2 rs1 000 rd 0110011 R add
0100000 rs2 rs1 000 rd 0110011 R sub
0000000 rs2 rs1 001 rd 0110011 R sll
0000000 rs2 rs1 010 rd 0110011 R slt
0000000 rs2 rs1 011 rd 0110011 R sltu
0000000 rs2 rs1 100 rd 0110011 R xor
0000000 rs2 rs1 101 rd 0110011 R srl
0100000 rs2 rs1 101 rd 0110011 R sra
0000000 rs2 rs1 110 rd 0110011 R or
0000000 rs2 rs1 111 rd 0110011 R and
0000 pred succ 00000 000 00000 0001111 I fence
0000 0000 0000 00000 001 00000 0001111 I fence.i
000000000000 00000 00 00000 1110011 I ecall
000000000000 00000 000 00000 1110011 I ebreak
csr rs1 001 rd 1110011 I csrrw
csr rs1 010 rd 1110011 I csrrs
csr rs1 011 rd 1110011 I csrrc
csr zimm 101 rd 1110011 I csrrwi
csr zimm 110 rd 1110011 I cssrrsi
csr zimm 111 rd 1110011 I csrrci
*/
#include <stdint.h>
// 寄存器枚举定义
typedef enum {
REG_X0, REG_X1, REG_X2, REG_X3, REG_X4, REG_X5, REG_X6, REG_X7,
REG_X8, REG_X9, REG_X10, REG_X11, REG_X12, REG_X13, REG_X14, REG_X15,
REG_X16, REG_X17, REG_X18, REG_X19, REG_X20, REG_X21, REG_X22, REG_X23,
REG_X24, REG_X25, REG_X26, REG_X27, REG_X28, REG_X29, REG_X30, REG_X31,
REG_ZERO = REG_X0, REG_RA = REG_X1, REG_SP = REG_X2, REG_GP = REG_X3,
REG_TP = REG_X4, REG_T0 = REG_X5, REG_T1 = REG_X6, REG_T2 = REG_X7,
REG_S0 = REG_X8, REG_S1 = REG_X9, REG_A0 = REG_X10, REG_A1 = REG_X11,
REG_A2 = REG_X12, REG_A3 = REG_X13, REG_A4 = REG_X14, REG_A5 = REG_X15,
REG_A6 = REG_X16, REG_A7 = REG_X17, REG_S2 = REG_X18, REG_S3 = REG_X19,
REG_S4 = REG_X20, REG_S5 = REG_X21, REG_S6 = REG_X22, REG_S7 = REG_X23,
REG_S8 = REG_X24, REG_S9 = REG_X25, REG_S10 = REG_X26, REG_S11 = REG_X27,
REG_T3 = REG_X28, REG_T4 = REG_X29, REG_T5 = REG_X30, REG_T6 = REG_X31,
} RV32Reg;
/******************** 立即数处理宏 ********************/
#define IMM_12BITS(imm) ((imm) & 0xFFF)
#define IMM_20BITS(imm) ((imm) & 0xFFFFF)
#define SHAMT_VAL(imm) ((imm) & 0x1F)
#define CSR_VAL(csr) ((csr) & 0xFFF)
// B型立即数编码[12|10:5|4:1|11]
#define ENCODE_B_IMM(imm) ( \
(((imm) >> 12) & 0x1) << 31 | /* imm[12:12] -> instr[31:31] */ \
(((imm) >> 5) & 0x3F) << 25 | /* imm[10:5] -> instr[30:25] */ \
(((imm) >> 1) & 0xF) << 8 | /* imm[4:1] -> instr[11:8] */ \
(((imm) >> 11) & 0x1) << 7) /* imm[11:11] -> instr[7:7] */
// J型立即数编码[20|10:1|11|19:12]W
#define ENCODE_J_IMM(imm) ( \
(((imm) >> 20) & 0x1) << 31 | /* imm[20:20] -> instr[31:31] */ \
(((imm) >> 1) & 0x3FF)<< 21 | /* imm[10:1] -> instr[30:21] */ \
(((imm) >> 11) & 0x1) << 20 | /* imm[11:11] -> instr[20:20] */ \
(((imm) >> 12) & 0xFF) << 12) /* imm[19:12] -> instr[19:12] */
/******************** 指令生成宏 ********************/
// R型指令宏
#define RV32_RTYPE(op, f3, f7, rd, rs1, rs2) (uint32_t)( \
(0x33 | ((rd) << 7) | ((f3) << 12) | ((rs1) << 15) | \
((rs2) << 20) | ((f7) << 25)) )
// I型指令宏
#define RV32_ITYPE(op, f3, rd, rs1, imm) (uint32_t)( \
(op | ((rd) << 7) | ((f3) << 12) | ((rs1) << 15) | \
(IMM_12BITS(imm) << 20)) )
// S型指令宏
#define RV32_STYPE(op, f3, rs1, rs2, imm) (uint32_t)( \
(op | ((IMM_12BITS(imm) & 0xFE0) << 20) | ((rs1) << 15) | \
((rs2) << 20) | ((f3) << 12) | ((IMM_12BITS(imm) & 0x1F) << 7)) )
// B型指令宏
#define RV32_BTYPE(op, f3, rs1, rs2, imm) (uint32_t)( \
(op | (ENCODE_B_IMM(imm)) | ((rs1) << 15) | \
((rs2) << 20) | ((f3) << 12)) )
// U型指令宏
#define RV32_UTYPE(op, rd, imm) (uint32_t)( \
(op | ((rd) << 7) | (IMM_20BITS((imm) >> 12) << 12)) )
// J型指令宏
#define RV32_JTYPE(op, rd, imm) (uint32_t)( \
(op | ((rd) << 7) | ENCODE_J_IMM(imm)) )
/******************** U-type ********************/
#define LUI(rd, imm) RV32_UTYPE(0x37, rd, imm)
#define AUIPC(rd, imm) RV32_UTYPE(0x17, rd, imm)
/******************** J-type ********************/
#define JAL(rd, imm) RV32_JTYPE(0x6F, rd, imm)
/******************** I-type ********************/
#define JALR(rd, rs1, imm) RV32_ITYPE(0x67, 0x0, rd, rs1, imm)
// Load instructions
#define LB(rd, rs1, imm) RV32_ITYPE(0x03, 0x0, rd, rs1, imm)
#define LH(rd, rs1, imm) RV32_ITYPE(0x03, 0x1, rd, rs1, imm)
#define LW(rd, rs1, imm) RV32_ITYPE(0x03, 0x2, rd, rs1, imm)
#define LBU(rd, rs1, imm) RV32_ITYPE(0x03, 0x4, rd, rs1, imm)
#define LHU(rd, rs1, imm) RV32_ITYPE(0x03, 0x5, rd, rs1, imm)
// Immediate arithmetic
#define ADDI(rd, rs1, imm) RV32_ITYPE(0x13, 0x0, rd, rs1, imm)
#define SLTI(rd, rs1, imm) RV32_ITYPE(0x13, 0x2, rd, rs1, imm)
#define SLTIU(rd, rs1, imm) RV32_ITYPE(0x13, 0x3, rd, rs1, imm)
#define XORI(rd, rs1, imm) RV32_ITYPE(0x13, 0x4, rd, rs1, imm)
#define ORI(rd, rs1, imm) RV32_ITYPE(0x13, 0x6, rd, rs1, imm)
#define ANDI(rd, rs1, imm) RV32_ITYPE(0x13, 0x7, rd, rs1, imm)
// Shift instructions
#define SLLI(rd, rs1, shamt) RV32_ITYPE(0x13, 0x1, rd, rs1, (0x00000000 | (shamt << 20)))
#define SRLI(rd, rs1, shamt) RV32_ITYPE(0x13, 0x5, rd, rs1, (0x00000000 | (shamt << 20)))
#define SRAI(rd, rs1, shamt) RV32_ITYPE(0x13, 0x5, rd, rs1, (0x40000000 | (shamt << 20)))
/******************** B-type ********************/
#define BEQ(rs1, rs2, imm) RV32_BTYPE(0x63, 0x0, rs1, rs2, imm)
#define BNE(rs1, rs2, imm) RV32_BTYPE(0x63, 0x1, rs1, rs2, imm)
#define BLT(rs1, rs2, imm) RV32_BTYPE(0x63, 0x4, rs1, rs2, imm)
#define BGE(rs1, rs2, imm) RV32_BTYPE(0x63, 0x5, rs1, rs2, imm)
#define BLTU(rs1, rs2, imm) RV32_BTYPE(0x63, 0x6, rs1, rs2, imm)
#define BGEU(rs1, rs2, imm) RV32_BTYPE(0x63, 0x7, rs1, rs2, imm)
/******************** S-type ********************/
#define SB(rs2, rs1, imm) RV32_STYPE(0x23, 0x0, rs1, rs2, imm)
#define SH(rs2, rs1, imm) RV32_STYPE(0x23, 0x1, rs1, rs2, imm)
#define SW(rs2, rs1, imm) RV32_STYPE(0x23, 0x2, rs1, rs2, imm)
/******************** R-type ********************/
#define ADD(rd, rs1, rs2) RV32_RTYPE(0x33, 0x0, 0x00, rd, rs1, rs2)
#define SUB(rd, rs1, rs2) RV32_RTYPE(0x33, 0x0, 0x20, rd, rs1, rs2)
#define SLL(rd, rs1, rs2) RV32_RTYPE(0x33, 0x1, 0x00, rd, rs1, rs2)
#define SLT(rd, rs1, rs2) RV32_RTYPE(0x33, 0x2, 0x00, rd, rs1, rs2)
#define SLTU(rd, rs1, rs2) RV32_RTYPE(0x33, 0x3, 0x00, rd, rs1, rs2)
#define XOR(rd, rs1, rs2) RV32_RTYPE(0x33, 0x4, 0x00, rd, rs1, rs2)
#define SRL(rd, rs1, rs2) RV32_RTYPE(0x33, 0x5, 0x00, rd, rs1, rs2)
#define SRA(rd, rs1, rs2) RV32_RTYPE(0x33, 0x5, 0x20, rd, rs1, rs2)
#define OR(rd, rs1, rs2) RV32_RTYPE(0x33, 0x6, 0x00, rd, rs1, rs2)
#define AND(rd, rs1, rs2) RV32_RTYPE(0x33, 0x7, 0x00, rd, rs1, rs2)
/******************** I-type (system) ********************/
#define FENCE(pred, succ) (uint32_t)( 0x0F | ((pred) << 23) | ((succ) << 27) )
#define FENCE_I() (uint32_t)( 0x100F )
#define ECALL() (uint32_t)( 0x73 )
#define EBREAK() (uint32_t)( 0x100073 )
// CSR instructions
#define CSRRW(rd, csr, rs) RV32_ITYPE(0x73, 0x1, rd, rs, CSR_VAL(csr))
#define CSRRS(rd, csr, rs) RV32_ITYPE(0x73, 0x2, rd, rs, CSR_VAL(csr))
#define CSRRC(rd, csr, rs) RV32_ITYPE(0x73, 0x3, rd, rs, CSR_VAL(csr))
#define CSRRWI(rd, csr, zimm) RV32_ITYPE(0x73, 0x5, rd, 0, (CSR_VAL(csr) | ((zimm) << 15)))
#define CSRRSI(rd, csr, zimm) RV32_ITYPE(0x73, 0x6, rd, 0, (CSR_VAL(csr) | ((zimm) << 15)))
#define CSRRCI(rd, csr, zimm) RV32_ITYPE(0x73, 0x7, rd, 0, (CSR_VAL(csr) | ((zimm) << 15)))
/* M-Extention */
#define MUL(rd, rs1, rs2) RV32_RTYPE(0x33, 0x0, 0x01, rd, rs1, rs2)
#define DIV(rd, rs1, rs2) RV32_RTYPE(0x33, 0x0, 0x05, rd, rs1, rs2)
#define REM(rd, rs1, rs2) RV32_RTYPE(0x33, 0x0, 0x07, rd, rs1, rs2)
/******************** Pseudo-instructions ********************/
// 伪指令
// nop (No operation)
#define NOP() ADDI(REG_X0, REG_X0, 0) // 无操作
// neg rd, rs (Two's complement of rs)
#define NEG(rd, rs) SUB(rd, REG_ZERO, rs) // 补码
// negw rd, rs (Two's complement word of rs)
#define NEGW(rd, rs) SUBW(rd, REG_ZERO, rs) // 字的补码
// snez rd, rs (Set if ≠ zero)
#define SNEZ(rd, rs) SLTU(rd, REG_X0, rs) // 非0则置位
// sltz rd, rs (Set if < zero)
#define SLTZ(rd, rs) SLT(rd, rs, REG_X0) // 小于0则置位
// sgtz rd, rs (Set if > zero)
#define SGTZ(rd, rs) SLT(rd, REG_X0, rs) // 大于0则置位
// beqz rs, offset (Branch if = zero)
#define BEQZ(rs, offset) BEQ(rs, REG_X0, offset) // 为0则转移
// bnez rs, offset (Branch if ≠ zero)
#define BNEZ(rs, offset) BNE(rs, REG_X0, offset) // 非0则转移
// blez rs, offset (Branch if ≤ zero)
#define BLEZ(rs, offset) BGE(REG_X0, rs, offset) // 小于等于0则转移
// bgez rs, offset (Branch if ≥ zero)
#define BGEZ(rs, offset) BGE(rs, REG_X0, offset) // 大于等于0则转移
// bltz rs, offset (Branch if < zero)
#define BLTZ(rs, offset) BLT(rs, REG_X0, offset) // 小于0则转移
// bgtz rs, offset (Branch if > zero)
#define BGTZ(rs, offset) BLT(REG_X0, rs, offset) // 大于0则转移
// j offset (Jump)
#define J(offset) JAL(REG_X0, offset) // 跳转
// jr rs (Jump register)
#define JR(rs) JALR(REG_X0, rs, 0) // 寄存器跳转
// ret (Return from subroutine)
#define RET() JALR(REG_X0, REG_RA, 0) // 从子过程返回
// tail offset (Tail call far-away subroutine)
#define TAIL_2(offset) AUIPC(REG_X6, offset), JAL(REG_X0, REG_X6, offset) // 尾调用远程子过程, 有2条指令
#define TAIL(offset) TAIL_2(offset) // Warning this have 2 instructions
// csrr csr, rd (Read CSR)
#define CSRR(csr, rd) CSRRS(rd, csr, REG_X0) // 读CSR寄存器
// csrw csr, rs (Write CSR)
#define CSR W(csr, rs) CSRRW(csr, REG_X0, rs) // 写CSR寄存器
// csrs csr, rs (Set bits in CSR)
#define CSRS(csr, rs) CSRRS(REG_X0, csr, rs) // CSR寄存器置零位
// csrrc csr, rs (Clear bits in CSR)
#define CSRC(csr, rs) CSRRC(REG_X0, csr, rs) // CSR寄存器清
// csrci csr, imm (Immediate clear bits in CSR)
#define CSRCI(csr, imm) CSRRCI(REG_X0, csr, imm) // 立即数清除CSR
// csrrwi csr, imm (Write CSR immediate)
#define CSRRWI2(csr, imm) CSRRWI(REG_X0, csr, imm) // 立即数写入CSR
// csrrsi csr, imm (Immediate set bits in CSR)
#define CSRRSI2(csr, imm) CSRRSI(REG_X0, csr, imm) // 立即数置位CSR
// csrrci csr, imm (Immediate clear bits in CSR)
#define CSRRCI2(csr, imm) CSRRCI(REG_X0, csr, imm) // 立即数清除CSR
// // frcsr rd (Read FP control/status register)
// #define FRC SR(rd) CSRRS(rd, FCSR, REG_X0) // 读取FP控制/状态寄存器
// // fscsr rs (Write FP control/status register)
// #define FSCSR(rs) CSRRW(REG_X0, FCSR, rs) // 写入FP控制/状态寄存器
// // frrm rd (Read FP rounding mode)
// #define FRRM(rd) CSRRS(rd, FRM, REG_X0) // 读取FP舍入模式
// // fsrm rs (Write FP rounding mode)
// #define FS RM(rs) CSRRW(REG_X0, FRM, rs) // 写入FP舍入模式
// // frflags rd (Read FP exception flags)
// #define FRFLAGS(rd) CSRRS(rd, FFLAGS, REG_X0) // 读取FP例外标志
// // fsflags rs (Write FP exception flags)
// #define FS FLAGS(rs) CSRRW(REG_X0, FFLAGS, rs) // 写入FP例外标志
// Myriad sequences
#define LI(rd, num) \
LUI(rd, num), \
ADDI(rd, rd, num)
#define MV(rd, rs) ADDI(rd, rs, 0)
#define NOT(rd, rs) XORI(rd, rs, -1)
#define SEQZ(rd, rs) SLTIU(rd, rs, 1)
#define SGT(rd, rs1, rs2) SLT(rd, rs2, rs1)
// TODO call have error when outof jalr
#define CALL(offset) \
AUIPC(REG_X1, REG_X0), \
JALR(REG_X1, REG_X1, offset)
#define CALL_ABS(addr) \
AUIPC(REG_X0, addr), \
JALR(REG_X1, REG_X0, addr)
#ifdef RISCV_VM_BUILDIN_ECALL
#define ECALL_PNT_INT(num) \
ADDI(REG_A0, REG_X0, num), \
ADDI(REG_A7, REG_X0, 0x1), \
ECALL()
#define ECALL_PNT_STR(str) \
ADDI(REG_A0, REG_X0, str), \
ADDI(REG_A7, REG_X0, 0x4), \
ECALL()
#define ECALL_EXIT2() \
ADDI(REG_A7, REG_X0, 93), \
ECALL()
#define ECALL_EXIT_ARG(errno) \
ADDI(REG_A0, REG_X0, errno), \
ECALL_EXIT2()
#define ECALL_EXIT() \
ADDI(REG_A7, REG_X0, 93), \
ECALL()
#define ECALL_SCAN_INT(int) \
ADDI(REG_A7, (1025 + 4)), \
ECALL()
#define ECALL_SCAN_STR(str) \
ADDI(REG_A0, REG_X0, str), \
ADDI(REG_A7, REG_X0, (1025 + 5)), \
ECALL()
#endif
#endif

View File

@ -1,8 +0,0 @@
CC = gcc
CFLAGS = -g -Wall
all = rv32-vm
CFLAGS += -DDEFAULT_FILE='\"flat.bin\"'
rv32-vm:
$(CC) $(CFLAGS) -g -o rv32-vm .\ripes-vm.c

View File

@ -1,520 +0,0 @@
// Copyright 2022 Charles Lohr, you may use this file or any portions herein under any of the BSD, MIT, or CC0 licenses.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "default64mbdtc.h"
// Just default RAM amount is 64MB.
uint32_t ram_amt = 64*1024*1024;
int fail_on_all_faults = 0;
static int64_t SimpleReadNumberInt( const char * number, int64_t defaultNumber );
static uint64_t GetTimeMicroseconds();
static void ResetKeyboardInput();
static void CaptureKeyboardInput();
static uint32_t HandleException( uint32_t ir, uint32_t retval );
static uint32_t HandleControlStore( uint32_t addy, uint32_t val );
static uint32_t HandleControlLoad( uint32_t addy );
static void HandleOtherCSRWrite( uint8_t * image, uint16_t csrno, uint32_t value );
static int32_t HandleOtherCSRRead( uint8_t * image, uint16_t csrno );
static void MiniSleep();
static int IsKBHit();
static int ReadKBByte();
// This is the functionality we want to override in the emulator.
// think of this as the way the emulator's processor is connected to the outside world.
#define MINIRV32WARN( x... ) printf( x );
#define MINIRV32_DECORATE static
#define MINI_RV32_RAM_SIZE ram_amt
#define MINIRV32_IMPLEMENTATION
#define MINIRV32_POSTEXEC( pc, ir, retval ) { if( retval > 0 ) { if( fail_on_all_faults ) { printf( "FAULT\n" ); return 3; } else retval = HandleException( ir, retval ); } }
#define MINIRV32_HANDLE_MEM_STORE_CONTROL( addy, val ) if( HandleControlStore( addy, val ) ) return val;
#define MINIRV32_HANDLE_MEM_LOAD_CONTROL( addy, rval ) rval = HandleControlLoad( addy );
#define MINIRV32_OTHERCSR_WRITE( csrno, value ) HandleOtherCSRWrite( image, csrno, value );
#define MINIRV32_OTHERCSR_READ( csrno, value ) value = HandleOtherCSRRead( image, csrno );
#include "mini-rv32ima.h"
uint8_t * ram_image = 0;
struct MiniRV32IMAState * core;
const char * kernel_command_line = 0;
static void DumpState( struct MiniRV32IMAState * core, uint8_t * ram_image );
int main( int argc, char ** argv )
{
int i;
long long instct = -1;
int show_help = 0;
int time_divisor = 1;
int fixed_update = 0;
int do_sleep = 1;
int single_step = 0;
int dtb_ptr = 0;
const char * image_file_name = 0;
const char * dtb_file_name = 0;
for( i = 1; i < argc; i++ )
{
const char * param = argv[i];
int param_continue = 0; // Can combine parameters, like -lpt x
do
{
if( param[0] == '-' || param_continue )
{
switch( param[1] )
{
case 'm': if( ++i < argc ) ram_amt = SimpleReadNumberInt( argv[i], ram_amt ); break;
case 'c': if( ++i < argc ) instct = SimpleReadNumberInt( argv[i], -1 ); break;
case 'k': if( ++i < argc ) kernel_command_line = argv[i]; break;
case 'f': image_file_name = (++i<argc)?argv[i]:0; break;
case 'b': dtb_file_name = (++i<argc)?argv[i]:0; break;
case 'l': param_continue = 1; fixed_update = 1; break;
case 'p': param_continue = 1; do_sleep = 0; break;
case 's': param_continue = 1; single_step = 1; break;
case 'd': param_continue = 1; fail_on_all_faults = 1; break;
case 't': if( ++i < argc ) time_divisor = SimpleReadNumberInt( argv[i], 1 ); break;
default:
if( param_continue )
param_continue = 0;
else
show_help = 1;
break;
}
}
else
{
show_help = 1;
break;
}
param++;
} while( param_continue );
}
if( show_help || image_file_name == 0 || time_divisor <= 0 )
{
fprintf( stderr, "./mini-rv32imaf [parameters]\n\t-m [ram amount]\n\t-f [running image]\n\t-k [kernel command line]\n\t-b [dtb file, or 'disable']\n\t-c instruction count\n\t-s single step with full processor state\n\t-t time divion base\n\t-l lock time base to instruction count\n\t-p disable sleep when wfi\n\t-d fail out immediately on all faults\n" );
return 1;
}
ram_image = malloc( ram_amt );
if( !ram_image )
{
fprintf( stderr, "Error: could not allocate system image.\n" );
return -4;
}
restart:
{
FILE * f = fopen( image_file_name, "rb" );
if( !f || ferror( f ) )
{
fprintf( stderr, "Error: \"%s\" not found\n", image_file_name );
return -5;
}
fseek( f, 0, SEEK_END );
long flen = ftell( f );
fseek( f, 0, SEEK_SET );
if( flen > ram_amt )
{
fprintf( stderr, "Error: Could not fit RAM image (%ld bytes) into %d\n", flen, ram_amt );
return -6;
}
memset( ram_image, 0, ram_amt );
if( fread( ram_image, flen, 1, f ) != 1)
{
fprintf( stderr, "Error: Could not load image.\n" );
return -7;
}
fclose( f );
if( dtb_file_name )
{
if( strcmp( dtb_file_name, "disable" ) == 0 )
{
// No DTB reading.
}
else
{
f = fopen( dtb_file_name, "rb" );
if( !f || ferror( f ) )
{
fprintf( stderr, "Error: \"%s\" not found\n", dtb_file_name );
return -5;
}
fseek( f, 0, SEEK_END );
long dtblen = ftell( f );
fseek( f, 0, SEEK_SET );
dtb_ptr = ram_amt - dtblen - sizeof( struct MiniRV32IMAState );
if( fread( ram_image + dtb_ptr, dtblen, 1, f ) != 1 )
{
fprintf( stderr, "Error: Could not open dtb \"%s\"\n", dtb_file_name );
return -9;
}
fclose( f );
}
}
else
{
// Load a default dtb.
dtb_ptr = ram_amt - sizeof(default64mbdtb) - sizeof( struct MiniRV32IMAState );
memcpy( ram_image + dtb_ptr, default64mbdtb, sizeof( default64mbdtb ) );
if( kernel_command_line )
{
strncpy( (char*)( ram_image + dtb_ptr + 0xc0 ), kernel_command_line, 54 );
}
}
}
CaptureKeyboardInput();
// The core lives at the end of RAM.
core = (struct MiniRV32IMAState *)(ram_image + ram_amt - sizeof( struct MiniRV32IMAState ));
core->pc = MINIRV32_RAM_IMAGE_OFFSET;
core->regs[10] = 0x00; //hart ID
core->regs[11] = dtb_ptr?(dtb_ptr+MINIRV32_RAM_IMAGE_OFFSET):0; //dtb_pa (Must be valid pointer) (Should be pointer to dtb)
core->extraflags |= 3; // Machine-mode.
if( dtb_file_name == 0 )
{
// Update system ram size in DTB (but if and only if we're using the default DTB)
// Warning - this will need to be updated if the skeleton DTB is ever modified.
uint32_t * dtb = (uint32_t*)(ram_image + dtb_ptr);
if( dtb[0x13c/4] == 0x00c0ff03 )
{
uint32_t validram = dtb_ptr;
dtb[0x13c/4] = (validram>>24) | ((( validram >> 16 ) & 0xff) << 8 ) | (((validram>>8) & 0xff ) << 16 ) | ( ( validram & 0xff) << 24 );
}
}
// Image is loaded.
uint64_t rt;
uint64_t lastTime = (fixed_update)?0:(GetTimeMicroseconds()/time_divisor);
int instrs_per_flip = single_step?1:1024;
for( rt = 0; rt < instct+1 || instct < 0; rt += instrs_per_flip )
{
uint64_t * this_ccount = ((uint64_t*)&core->cyclel);
uint32_t elapsedUs = 0;
if( fixed_update )
elapsedUs = *this_ccount / time_divisor - lastTime;
else
elapsedUs = GetTimeMicroseconds()/time_divisor - lastTime;
lastTime += elapsedUs;
if( single_step )
DumpState( core, ram_image);
int ret = MiniRV32IMAStep( core, ram_image, 0, elapsedUs, instrs_per_flip ); // Execute upto 1024 cycles before breaking out.
switch( ret )
{
case 0: break;
case 1: if( do_sleep ) MiniSleep(); *this_ccount += instrs_per_flip; break;
case 3: instct = 0; break;
case 0x7777: goto restart; //syscon code for restart
case 0x5555: printf( "POWEROFF@0x%08x%08x\n", core->cycleh, core->cyclel ); return 0; //syscon code for power-off
default: printf( "Unknown failure\n" ); break;
}
}
DumpState( core, ram_image);
}
//////////////////////////////////////////////////////////////////////////
// Platform-specific functionality
//////////////////////////////////////////////////////////////////////////
#if defined(WINDOWS) || defined(WIN32) || defined(_WIN32)
#include <windows.h>
#include <conio.h>
#define strtoll _strtoi64
static void CaptureKeyboardInput()
{
system(""); // Poorly documented tick: Enable VT100 Windows mode.
}
static void ResetKeyboardInput()
{
}
static void MiniSleep()
{
Sleep(1);
}
static uint64_t GetTimeMicroseconds()
{
static LARGE_INTEGER lpf;
LARGE_INTEGER li;
if( !lpf.QuadPart )
QueryPerformanceFrequency( &lpf );
QueryPerformanceCounter( &li );
return ((uint64_t)li.QuadPart * 1000000LL) / (uint64_t)lpf.QuadPart;
}
static int IsKBHit()
{
return _kbhit();
}
static int ReadKBByte()
{
// This code is kind of tricky, but used to convert windows arrow keys
// to VT100 arrow keys.
static int is_escape_sequence = 0;
int r;
if( is_escape_sequence == 1 )
{
is_escape_sequence++;
return '[';
}
r = _getch();
if( is_escape_sequence )
{
is_escape_sequence = 0;
switch( r )
{
case 'H': return 'A'; // Up
case 'P': return 'B'; // Down
case 'K': return 'D'; // Left
case 'M': return 'C'; // Right
case 'G': return 'H'; // Home
case 'O': return 'F'; // End
default: return r; // Unknown code.
}
}
else
{
switch( r )
{
case 13: return 10; //cr->lf
case 224: is_escape_sequence = 1; return 27; // Escape arrow keys
default: return r;
}
}
}
#else
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
static void CtrlC()
{
DumpState( core, ram_image);
exit( 0 );
}
// Override keyboard, so we can capture all keyboard input for the VM.
static void CaptureKeyboardInput()
{
// Hook exit, because we want to re-enable keyboard.
atexit(ResetKeyboardInput);
signal(SIGINT, CtrlC);
struct termios term;
tcgetattr(0, &term);
term.c_lflag &= ~(ICANON | ECHO); // Disable echo as well
tcsetattr(0, TCSANOW, &term);
}
static void ResetKeyboardInput()
{
// Re-enable echo, etc. on keyboard.
struct termios term;
tcgetattr(0, &term);
term.c_lflag |= ICANON | ECHO;
tcsetattr(0, TCSANOW, &term);
}
static void MiniSleep()
{
usleep(500);
}
static uint64_t GetTimeMicroseconds()
{
struct timeval tv;
gettimeofday( &tv, 0 );
return tv.tv_usec + ((uint64_t)(tv.tv_sec)) * 1000000LL;
}
static int is_eofd;
static int ReadKBByte()
{
if( is_eofd ) return 0xffffffff;
char rxchar = 0;
int rread = read(fileno(stdin), (char*)&rxchar, 1);
if( rread > 0 ) // Tricky: getchar can't be used with arrow keys.
return rxchar;
else
return -1;
}
static int IsKBHit()
{
if( is_eofd ) return -1;
int byteswaiting;
ioctl(0, FIONREAD, &byteswaiting);
if( !byteswaiting && write( fileno(stdin), 0, 0 ) != 0 ) { is_eofd = 1; return -1; } // Is end-of-file for
return !!byteswaiting;
}
#endif
//////////////////////////////////////////////////////////////////////////
// Rest of functions functionality
//////////////////////////////////////////////////////////////////////////
static uint32_t HandleException( uint32_t ir, uint32_t code )
{
// Weird opcode emitted by duktape on exit.
if( code == 3 )
{
// Could handle other opcodes here.
}
return code;
}
static uint32_t HandleControlStore( uint32_t addy, uint32_t val )
{
if( addy == 0x10000000 ) //UART 8250 / 16550 Data Buffer
{
printf( "%c", val );
fflush( stdout );
}
else if( addy == 0x11004004 ) //CLNT
core->timermatchh = val;
else if( addy == 0x11004000 ) //CLNT
core->timermatchl = val;
else if( addy == 0x11100000 ) //SYSCON (reboot, poweroff, etc.)
{
core->pc = core->pc + 4;
return val; // NOTE: PC will be PC of Syscon.
}
return 0;
}
static uint32_t HandleControlLoad( uint32_t addy )
{
// Emulating a 8250 / 16550 UART
if( addy == 0x10000005 )
return 0x60 | IsKBHit();
else if( addy == 0x10000000 && IsKBHit() )
return ReadKBByte();
else if( addy == 0x1100bffc ) // https://chromitem-soc.readthedocs.io/en/latest/clint.html
return core->timerh;
else if( addy == 0x1100bff8 )
return core->timerl;
return 0;
}
static void HandleOtherCSRWrite( uint8_t * image, uint16_t csrno, uint32_t value )
{
if( csrno == 0x136 )
{
printf( "%d", value ); fflush( stdout );
}
if( csrno == 0x137 )
{
printf( "%08x", value ); fflush( stdout );
}
else if( csrno == 0x138 )
{
//Print "string"
uint32_t ptrstart = value - MINIRV32_RAM_IMAGE_OFFSET;
uint32_t ptrend = ptrstart;
if( ptrstart >= ram_amt )
printf( "DEBUG PASSED INVALID PTR (%08x)\n", value );
while( ptrend < ram_amt )
{
if( image[ptrend] == 0 ) break;
ptrend++;
}
if( ptrend != ptrstart )
fwrite( image + ptrstart, ptrend - ptrstart, 1, stdout );
}
else if( csrno == 0x139 )
{
putchar( value ); fflush( stdout );
}
}
static int32_t HandleOtherCSRRead( uint8_t * image, uint16_t csrno )
{
if( csrno == 0x140 )
{
if( !IsKBHit() ) return -1;
return ReadKBByte();
}
return 0;
}
static int64_t SimpleReadNumberInt( const char * number, int64_t defaultNumber )
{
if( !number || !number[0] ) return defaultNumber;
int radix = 10;
if( number[0] == '0' )
{
char nc = number[1];
number+=2;
if( nc == 0 ) return 0;
else if( nc == 'x' ) radix = 16;
else if( nc == 'b' ) radix = 2;
else { number--; radix = 8; }
}
char * endptr;
uint64_t ret = strtoll( number, &endptr, radix );
if( endptr == number )
{
return defaultNumber;
}
else
{
return ret;
}
}
static void DumpState( struct MiniRV32IMAState * core, uint8_t * ram_image )
{
uint32_t pc = core->pc;
uint32_t pc_offset = pc - MINIRV32_RAM_IMAGE_OFFSET;
uint32_t ir = 0;
printf( "PC: %08x ", pc );
if( pc_offset >= 0 && pc_offset < ram_amt - 3 )
{
ir = *((uint32_t*)(&((uint8_t*)ram_image)[pc_offset]));
printf( "[0x%08x] ", ir );
}
else
printf( "[xxxxxxxxxx] " );
uint32_t * regs = core->regs;
printf( "Z:%08x ra:%08x sp:%08x gp:%08x tp:%08x t0:%08x t1:%08x t2:%08x s0:%08x s1:%08x a0:%08x a1:%08x a2:%08x a3:%08x a4:%08x a5:%08x ",
regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7],
regs[8], regs[9], regs[10], regs[11], regs[12], regs[13], regs[14], regs[15] );
printf( "a6:%08x a7:%08x s2:%08x s3:%08x s4:%08x s5:%08x s6:%08x s7:%08x s8:%08x s9:%08x s10:%08x s11:%08x t3:%08x t4:%08x t5:%08x t6:%08x\n",
regs[16], regs[17], regs[18], regs[19], regs[20], regs[21], regs[22], regs[23],
regs[24], regs[25], regs[26], regs[27], regs[28], regs[29], regs[30], regs[31] );
}

View File

@ -1,547 +0,0 @@
// Copyright 2022 Charles Lohr, you may use this file or any portions herein under any of the BSD, MIT, or CC0 licenses.
#ifndef _MINI_RV32IMAH_H
#define _MINI_RV32IMAH_H
/**
To use mini-rv32ima.h for the bare minimum, the following:
#define MINI_RV32_RAM_SIZE ram_amt
#define MINIRV32_IMPLEMENTATION
#include "mini-rv32ima.h"
Though, that's not _that_ interesting. You probably want I/O!
Notes:
* There is a dedicated CLNT at 0x10000000.
* There is free MMIO from there to 0x12000000.
* You can put things like a UART, or whatever there.
* Feel free to override any of the functionality with macros.
*/
#ifndef MINIRV32WARN
#define MINIRV32WARN( x... );
#endif
#ifndef MINIRV32_DECORATE
#define MINIRV32_DECORATE static
#endif
#ifndef MINIRV32_RAM_IMAGE_OFFSET
#define MINIRV32_RAM_IMAGE_OFFSET 0x80000000
#endif
#ifndef MINIRV32_MMIO_RANGE
#define MINIRV32_MMIO_RANGE(n) (0x10000000 <= (n) && (n) < 0x12000000)
#endif
#ifndef MINIRV32_POSTEXEC
#define MINIRV32_POSTEXEC(...);
#endif
#ifndef MINIRV32_HANDLE_MEM_STORE_CONTROL
#define MINIRV32_HANDLE_MEM_STORE_CONTROL(...);
#endif
#ifndef MINIRV32_HANDLE_MEM_LOAD_CONTROL
#define MINIRV32_HANDLE_MEM_LOAD_CONTROL(...);
#endif
#ifndef MINIRV32_OTHERCSR_WRITE
#define MINIRV32_OTHERCSR_WRITE(...);
#endif
#ifndef MINIRV32_OTHERCSR_READ
#define MINIRV32_OTHERCSR_READ(...);
#endif
#ifndef MINIRV32_CUSTOM_MEMORY_BUS
#define MINIRV32_STORE4( ofs, val ) *(uint32_t*)(image + ofs) = val
#define MINIRV32_STORE2( ofs, val ) *(uint16_t*)(image + ofs) = val
#define MINIRV32_STORE1( ofs, val ) *(uint8_t*)(image + ofs) = val
#define MINIRV32_LOAD4( ofs ) *(uint32_t*)(image + ofs)
#define MINIRV32_LOAD2( ofs ) *(uint16_t*)(image + ofs)
#define MINIRV32_LOAD1( ofs ) *(uint8_t*)(image + ofs)
#define MINIRV32_LOAD2_SIGNED( ofs ) *(int16_t*)(image + ofs)
#define MINIRV32_LOAD1_SIGNED( ofs ) *(int8_t*)(image + ofs)
#endif
// As a note: We quouple-ify these, because in HLSL, we will be operating with
// uint4's. We are going to uint4 data to/from system RAM.
//
// We're going to try to keep the full processor state to 12 x uint4.
struct MiniRV32IMAState
{
uint32_t regs[32];
uint32_t pc;
uint32_t mstatus;
uint32_t cyclel;
uint32_t cycleh;
uint32_t timerl;
uint32_t timerh;
uint32_t timermatchl;
uint32_t timermatchh;
uint32_t mscratch;
uint32_t mtvec;
uint32_t mie;
uint32_t mip;
uint32_t mepc;
uint32_t mtval;
uint32_t mcause;
// Note: only a few bits are used. (Machine = 3, User = 0)
// Bits 0..1 = privilege.
// Bit 2 = WFI (Wait for interrupt)
// Bit 3+ = Load/Store reservation LSBs.
uint32_t extraflags;
};
#ifndef MINIRV32_STEPPROTO
MINIRV32_DECORATE int32_t MiniRV32IMAStep( struct MiniRV32IMAState * state, uint8_t * image, uint32_t vProcAddress, uint32_t elapsedUs, int count );
#endif
#ifdef MINIRV32_IMPLEMENTATION
#ifndef MINIRV32_CUSTOM_INTERNALS
#define CSR( x ) state->x
#define SETCSR( x, val ) { state->x = val; }
#define REG( x ) state->regs[x]
#define REGSET( x, val ) { state->regs[x] = val; }
#endif
#ifndef MINIRV32_STEPPROTO
MINIRV32_DECORATE int32_t MiniRV32IMAStep( struct MiniRV32IMAState * state, uint8_t * image, uint32_t vProcAddress, uint32_t elapsedUs, int count )
#else
MINIRV32_STEPPROTO
#endif
{
uint32_t new_timer = CSR( timerl ) + elapsedUs;
if( new_timer < CSR( timerl ) ) CSR( timerh )++;
CSR( timerl ) = new_timer;
// Handle Timer interrupt.
if( ( CSR( timerh ) > CSR( timermatchh ) || ( CSR( timerh ) == CSR( timermatchh ) && CSR( timerl ) > CSR( timermatchl ) ) ) && ( CSR( timermatchh ) || CSR( timermatchl ) ) )
{
CSR( extraflags ) &= ~4; // Clear WFI
CSR( mip ) |= 1<<7; //MTIP of MIP // https://stackoverflow.com/a/61916199/2926815 Fire interrupt.
}
else
CSR( mip ) &= ~(1<<7);
// If WFI, don't run processor.
if( CSR( extraflags ) & 4 )
return 1;
uint32_t trap = 0;
uint32_t rval = 0;
uint32_t pc = CSR( pc );
uint32_t cycle = CSR( cyclel );
if( ( CSR( mip ) & (1<<7) ) && ( CSR( mie ) & (1<<7) /*mtie*/ ) && ( CSR( mstatus ) & 0x8 /*mie*/) )
{
// Timer interrupt.
trap = 0x80000007;
pc -= 4;
}
else // No timer interrupt? Execute a bunch of instructions.
for( int icount = 0; icount < count; icount++ )
{
uint32_t ir = 0;
rval = 0;
cycle++;
uint32_t ofs_pc = pc - MINIRV32_RAM_IMAGE_OFFSET;
if( ofs_pc >= MINI_RV32_RAM_SIZE )
{
trap = 1 + 1; // Handle access violation on instruction read.
break;
}
else if( ofs_pc & 3 )
{
trap = 1 + 0; //Handle PC-misaligned access
break;
}
else
{
ir = MINIRV32_LOAD4( ofs_pc );
uint32_t rdid = (ir >> 7) & 0x1f;
switch( ir & 0x7f )
{
case 0x37: // LUI (0b0110111)
rval = ( ir & 0xfffff000 );
break;
case 0x17: // AUIPC (0b0010111)
rval = pc + ( ir & 0xfffff000 );
break;
case 0x6F: // JAL (0b1101111)
{
int32_t reladdy = ((ir & 0x80000000)>>11) | ((ir & 0x7fe00000)>>20) | ((ir & 0x00100000)>>9) | ((ir&0x000ff000));
if( reladdy & 0x00100000 ) reladdy |= 0xffe00000; // Sign extension.
rval = pc + 4;
pc = pc + reladdy - 4;
break;
}
case 0x67: // JALR (0b1100111)
{
uint32_t imm = ir >> 20;
int32_t imm_se = imm | (( imm & 0x800 )?0xfffff000:0);
rval = pc + 4;
pc = ( (REG( (ir >> 15) & 0x1f ) + imm_se) & ~1) - 4;
break;
}
case 0x63: // Branch (0b1100011)
{
uint32_t immm4 = ((ir & 0xf00)>>7) | ((ir & 0x7e000000)>>20) | ((ir & 0x80) << 4) | ((ir >> 31)<<12);
if( immm4 & 0x1000 ) immm4 |= 0xffffe000;
int32_t rs1 = REG((ir >> 15) & 0x1f);
int32_t rs2 = REG((ir >> 20) & 0x1f);
immm4 = pc + immm4 - 4;
rdid = 0;
switch( ( ir >> 12 ) & 0x7 )
{
// BEQ, BNE, BLT, BGE, BLTU, BGEU
case 0: if( rs1 == rs2 ) pc = immm4; break;
case 1: if( rs1 != rs2 ) pc = immm4; break;
case 4: if( rs1 < rs2 ) pc = immm4; break;
case 5: if( rs1 >= rs2 ) pc = immm4; break; //BGE
case 6: if( (uint32_t)rs1 < (uint32_t)rs2 ) pc = immm4; break; //BLTU
case 7: if( (uint32_t)rs1 >= (uint32_t)rs2 ) pc = immm4; break; //BGEU
default: trap = (2+1);
}
break;
}
case 0x03: // Load (0b0000011)
{
uint32_t rs1 = REG((ir >> 15) & 0x1f);
uint32_t imm = ir >> 20;
int32_t imm_se = imm | (( imm & 0x800 )?0xfffff000:0);
uint32_t rsval = rs1 + imm_se;
rsval -= MINIRV32_RAM_IMAGE_OFFSET;
if( rsval >= MINI_RV32_RAM_SIZE-3 )
{
rsval += MINIRV32_RAM_IMAGE_OFFSET;
if( MINIRV32_MMIO_RANGE( rsval ) ) // UART, CLNT
{
MINIRV32_HANDLE_MEM_LOAD_CONTROL( rsval, rval );
}
else
{
trap = (5+1);
rval = rsval;
}
}
else
{
switch( ( ir >> 12 ) & 0x7 )
{
//LB, LH, LW, LBU, LHU
case 0: rval = MINIRV32_LOAD1_SIGNED( rsval ); break;
case 1: rval = MINIRV32_LOAD2_SIGNED( rsval ); break;
case 2: rval = MINIRV32_LOAD4( rsval ); break;
case 4: rval = MINIRV32_LOAD1( rsval ); break;
case 5: rval = MINIRV32_LOAD2( rsval ); break;
default: trap = (2+1);
}
}
break;
}
case 0x23: // Store 0b0100011
{
uint32_t rs1 = REG((ir >> 15) & 0x1f);
uint32_t rs2 = REG((ir >> 20) & 0x1f);
uint32_t addy = ( ( ir >> 7 ) & 0x1f ) | ( ( ir & 0xfe000000 ) >> 20 );
if( addy & 0x800 ) addy |= 0xfffff000;
addy += rs1 - MINIRV32_RAM_IMAGE_OFFSET;
rdid = 0;
if( addy >= MINI_RV32_RAM_SIZE-3 )
{
addy += MINIRV32_RAM_IMAGE_OFFSET;
if( MINIRV32_MMIO_RANGE( addy ) )
{
MINIRV32_HANDLE_MEM_STORE_CONTROL( addy, rs2 );
}
else
{
trap = (7+1); // Store access fault.
rval = addy;
}
}
else
{
switch( ( ir >> 12 ) & 0x7 )
{
//SB, SH, SW
case 0: MINIRV32_STORE1( addy, rs2 ); break;
case 1: MINIRV32_STORE2( addy, rs2 ); break;
case 2: MINIRV32_STORE4( addy, rs2 ); break;
default: trap = (2+1);
}
}
break;
}
case 0x13: // Op-immediate 0b0010011
case 0x33: // Op 0b0110011
{
uint32_t imm = ir >> 20;
imm = imm | (( imm & 0x800 )?0xfffff000:0);
uint32_t rs1 = REG((ir >> 15) & 0x1f);
uint32_t is_reg = !!( ir & 0x20 );
uint32_t rs2 = is_reg ? REG(imm & 0x1f) : imm;
if( is_reg && ( ir & 0x02000000 ) )
{
switch( (ir>>12)&7 ) //0x02000000 = RV32M
{
case 0: rval = rs1 * rs2; break; // MUL
#ifndef CUSTOM_MULH // If compiling on a system that doesn't natively, or via libgcc support 64-bit math.
case 1: rval = ((int64_t)((int32_t)rs1) * (int64_t)((int32_t)rs2)) >> 32; break; // MULH
case 2: rval = ((int64_t)((int32_t)rs1) * (uint64_t)rs2) >> 32; break; // MULHSU
case 3: rval = ((uint64_t)rs1 * (uint64_t)rs2) >> 32; break; // MULHU
#else
CUSTOM_MULH
#endif
case 4: if( rs2 == 0 ) rval = -1; else rval = ((int32_t)rs1 == INT32_MIN && (int32_t)rs2 == -1) ? rs1 : ((int32_t)rs1 / (int32_t)rs2); break; // DIV
case 5: if( rs2 == 0 ) rval = 0xffffffff; else rval = rs1 / rs2; break; // DIVU
case 6: if( rs2 == 0 ) rval = rs1; else rval = ((int32_t)rs1 == INT32_MIN && (int32_t)rs2 == -1) ? 0 : ((uint32_t)((int32_t)rs1 % (int32_t)rs2)); break; // REM
case 7: if( rs2 == 0 ) rval = rs1; else rval = rs1 % rs2; break; // REMU
}
}
else
{
switch( (ir>>12)&7 ) // These could be either op-immediate or op commands. Be careful.
{
case 0: rval = (is_reg && (ir & 0x40000000) ) ? ( rs1 - rs2 ) : ( rs1 + rs2 ); break;
case 1: rval = rs1 << (rs2 & 0x1F); break;
case 2: rval = (int32_t)rs1 < (int32_t)rs2; break;
case 3: rval = rs1 < rs2; break;
case 4: rval = rs1 ^ rs2; break;
case 5: rval = (ir & 0x40000000 ) ? ( ((int32_t)rs1) >> (rs2 & 0x1F) ) : ( rs1 >> (rs2 & 0x1F) ); break;
case 6: rval = rs1 | rs2; break;
case 7: rval = rs1 & rs2; break;
}
}
break;
}
case 0x0f: // 0b0001111
rdid = 0; // fencetype = (ir >> 12) & 0b111; We ignore fences in this impl.
break;
case 0x73: // Zifencei+Zicsr (0b1110011)
{
uint32_t csrno = ir >> 20;
uint32_t microop = ( ir >> 12 ) & 0x7;
if( (microop & 3) ) // It's a Zicsr function.
{
int rs1imm = (ir >> 15) & 0x1f;
uint32_t rs1 = REG(rs1imm);
uint32_t writeval = rs1;
// https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf
// Generally, support for Zicsr
switch( csrno )
{
case 0x340: rval = CSR( mscratch ); break;
case 0x305: rval = CSR( mtvec ); break;
case 0x304: rval = CSR( mie ); break;
case 0xC00: rval = cycle; break;
case 0x344: rval = CSR( mip ); break;
case 0x341: rval = CSR( mepc ); break;
case 0x300: rval = CSR( mstatus ); break; //mstatus
case 0x342: rval = CSR( mcause ); break;
case 0x343: rval = CSR( mtval ); break;
case 0xf11: rval = 0xff0ff0ff; break; //mvendorid
case 0x301: rval = 0x40401101; break; //misa (XLEN=32, IMA+X)
//case 0x3B0: rval = 0; break; //pmpaddr0
//case 0x3a0: rval = 0; break; //pmpcfg0
//case 0xf12: rval = 0x00000000; break; //marchid
//case 0xf13: rval = 0x00000000; break; //mimpid
//case 0xf14: rval = 0x00000000; break; //mhartid
default:
MINIRV32_OTHERCSR_READ( csrno, rval );
break;
}
switch( microop )
{
case 1: writeval = rs1; break; //CSRRW
case 2: writeval = rval | rs1; break; //CSRRS
case 3: writeval = rval & ~rs1; break; //CSRRC
case 5: writeval = rs1imm; break; //CSRRWI
case 6: writeval = rval | rs1imm; break; //CSRRSI
case 7: writeval = rval & ~rs1imm; break; //CSRRCI
}
switch( csrno )
{
case 0x340: SETCSR( mscratch, writeval ); break;
case 0x305: SETCSR( mtvec, writeval ); break;
case 0x304: SETCSR( mie, writeval ); break;
case 0x344: SETCSR( mip, writeval ); break;
case 0x341: SETCSR( mepc, writeval ); break;
case 0x300: SETCSR( mstatus, writeval ); break; //mstatus
case 0x342: SETCSR( mcause, writeval ); break;
case 0x343: SETCSR( mtval, writeval ); break;
//case 0x3a0: break; //pmpcfg0
//case 0x3B0: break; //pmpaddr0
//case 0xf11: break; //mvendorid
//case 0xf12: break; //marchid
//case 0xf13: break; //mimpid
//case 0xf14: break; //mhartid
//case 0x301: break; //misa
default:
MINIRV32_OTHERCSR_WRITE( csrno, writeval );
break;
}
}
else if( microop == 0x0 ) // "SYSTEM" 0b000
{
rdid = 0;
if( ( ( csrno & 0xff ) == 0x02 ) ) // MRET
{
//https://raw.githubusercontent.com/riscv/virtual-memory/main/specs/663-Svpbmt.pdf
//Table 7.6. MRET then in mstatus/mstatush sets MPV=0, MPP=0, MIE=MPIE, and MPIE=1. La
// Should also update mstatus to reflect correct mode.
uint32_t startmstatus = CSR( mstatus );
uint32_t startextraflags = CSR( extraflags );
SETCSR( mstatus , (( startmstatus & 0x80) >> 4) | ((startextraflags&3) << 11) | 0x80 );
SETCSR( extraflags, (startextraflags & ~3) | ((startmstatus >> 11) & 3) );
pc = CSR( mepc ) -4;
} else {
switch (csrno) {
case 0:
#ifndef ECALL_HANDLER
trap = ( CSR( extraflags ) & 3) ? (11+1) : (8+1); // ECALL; 8 = "Environment call from U-mode"; 11 = "Environment call from M-mode"
#else
ECALL_HANDLER(state);
trap = 0;
#endif
break;
case 1:
trap = (3+1); break; // EBREAK 3 = "Breakpoint"
case 0x105: //WFI (Wait for interrupts)
CSR( mstatus ) |= 8; //Enable interrupts
CSR( extraflags ) |= 4; //Infor environment we want to go to sleep.
SETCSR( pc, pc + 4 );
return 1;
default:
trap = (2+1); break; // Illegal opcode.
}
}
}
else
trap = (2+1); // Note micrrop 0b100 == undefined.
break;
}
case 0x2f: // RV32A (0b00101111)
{
uint32_t rs1 = REG((ir >> 15) & 0x1f);
uint32_t rs2 = REG((ir >> 20) & 0x1f);
uint32_t irmid = ( ir>>27 ) & 0x1f;
rs1 -= MINIRV32_RAM_IMAGE_OFFSET;
// We don't implement load/store from UART or CLNT with RV32A here.
if( rs1 >= MINI_RV32_RAM_SIZE-3 )
{
trap = (7+1); //Store/AMO access fault
rval = rs1 + MINIRV32_RAM_IMAGE_OFFSET;
}
else
{
rval = MINIRV32_LOAD4( rs1 );
// Referenced a little bit of https://github.com/franzflasch/riscv_em/blob/master/src/core/core.c
uint32_t dowrite = 1;
switch( irmid )
{
case 2: //LR.W (0b00010)
dowrite = 0;
CSR( extraflags ) = (CSR( extraflags ) & 0x07) | (rs1<<3);
break;
case 3: //SC.W (0b00011) (Make sure we have a slot, and, it's valid)
rval = ( CSR( extraflags ) >> 3 != ( rs1 & 0x1fffffff ) ); // Validate that our reservation slot is OK.
dowrite = !rval; // Only write if slot is valid.
break;
case 1: break; //AMOSWAP.W (0b00001)
case 0: rs2 += rval; break; //AMOADD.W (0b00000)
case 4: rs2 ^= rval; break; //AMOXOR.W (0b00100)
case 12: rs2 &= rval; break; //AMOAND.W (0b01100)
case 8: rs2 |= rval; break; //AMOOR.W (0b01000)
case 16: rs2 = ((int32_t)rs2<(int32_t)rval)?rs2:rval; break; //AMOMIN.W (0b10000)
case 20: rs2 = ((int32_t)rs2>(int32_t)rval)?rs2:rval; break; //AMOMAX.W (0b10100)
case 24: rs2 = (rs2<rval)?rs2:rval; break; //AMOMINU.W (0b11000)
case 28: rs2 = (rs2>rval)?rs2:rval; break; //AMOMAXU.W (0b11100)
default: trap = (2+1); dowrite = 0; break; //Not supported.
}
if( dowrite ) MINIRV32_STORE4( rs1, rs2 );
}
break;
}
default: trap = (2+1); // Fault: Invalid opcode.
}
// If there was a trap, do NOT allow register writeback.
if( trap ) {
SETCSR( pc, pc );
MINIRV32_POSTEXEC( pc, ir, trap );
break;
}
if( rdid )
{
REGSET( rdid, rval ); // Write back register.
}
}
MINIRV32_POSTEXEC( pc, ir, trap );
pc += 4;
}
// Handle traps and interrupts.
if( trap )
{
if( trap & 0x80000000 ) // If prefixed with 1 in MSB, it's an interrupt, not a trap.
{
SETCSR( mcause, trap );
SETCSR( mtval, 0 );
pc += 4; // PC needs to point to where the PC will return to.
}
else
{
SETCSR( mcause, trap - 1 );
SETCSR( mtval, (trap > 5 && trap <= 8)? rval : pc );
}
SETCSR( mepc, pc ); //TRICKY: The kernel advances mepc automatically.
//CSR( mstatus ) & 8 = MIE, & 0x80 = MPIE
// On an interrupt, the system moves current MIE into MPIE
SETCSR( mstatus, (( CSR( mstatus ) & 0x08) << 4) | (( CSR( extraflags ) & 3 ) << 11) );
pc = (CSR( mtvec ) - 4);
// If trapping, always enter machine mode.
CSR( extraflags ) |= 3;
trap = 0;
pc += 4;
}
if( CSR( cyclel ) > cycle ) CSR( cycleh )++;
SETCSR( cyclel, cycle );
SETCSR( pc, pc );
return 0;
}
#endif
#endif

View File

@ -1,192 +0,0 @@
# riscv_emufun (mini-rv32ima)
Click below for the YouTube video introducing this project:
[![Writing a Really Tiny RISC-V Emulator](https://img.youtube.com/vi/YT5vB3UqU_E/0.jpg)](https://www.youtube.com/watch?v=YT5vB3UqU_E) [![But Will It Run Doom?](https://img.youtube.com/vi/uZMNK17VCMU/0.jpg)](https://www.youtube.com/watch?v=uZMNK17VCMU)
## What
mini-rv32ima is a single-file-header, [mini-rv32ima.h](https://github.com/cnlohr/riscv_emufun/blob/master/mini-rv32ima/mini-rv32ima.h), in the [STB Style library](https://github.com/nothings/stb) that:
* Implements a RISC-V **rv32ima/Zifencei†+Zicsr** (and partial su), with CLINT and MMIO.
* Is about **400 lines** of actual code.
* Has **no dependencies**, not even libc.
* Is **easily extensible**. So you can easily add CSRs, instructions, MMIO, etc!
* Is pretty **performant**. (~450 coremark on my laptop, about 1/2 the speed of QEMU)
* Is human-readable and in **basic C** code.
* Is "**incomplete**" in that it didn't implement the tons of the spec that Linux doesn't (and you shouldn't) use.
* Is trivially **embeddable** in applications.
It has a [demo wrapper](https://github.com/cnlohr/riscv_emufun/blob/master/mini-rv32ima/mini-rv32ima.c) that:
* Implements a CLI, SYSCON, UART, DTB and Kernel image loading.
* And it only around **250 lines** of code, itself.
* Compiles down to a **~18kB executable** and only relies on libc.
†: Zifence+RV32A are stubbed. So, tweaks will need to be made if you want to emulate a multiprocessor system with this emulator.
Just see the `mini-rv32ima` folder.
It's "fully functional" now in that I can run Linux, apps, etc. Compile flat binaries and drop them in an image.
## Why
I'm working on a really really simple C Risc-V emulator. So simple it doesn't even have an MMU (Memory Management Unit). I have a few goals, they include:
* Furthering RV32-NOMMU work to improve Linux support for RV32-NOMMU. (Imagine if we could run Linux on the $1 ESP32-C3)
* Learning more about RV32 and writing emulators.
* Being further inspired by @pimaker's amazing work on [Running Linux in a Pixel Shader](https://blog.pimaker.at/texts/rvc1/) and having the sneaking suspicion performance could be even better!
* Hoping to port it to some weird places.
* Understand the *most simplistic* system you can run Linux on and trying to push that boundary.
* Continue to include my [education of people about assembly language](https://www.youtube.com/watch?v=Gelf0AyVGy4).
## How
Windows instructions (Just playing with the image)
* Clone this repo.
* Install or have TinyCC. [Powershell Installer](https://github.com/cntools/Install-TCC) or [Regular Windows Installer](https://github.com/cnlohr/tinycc-win64-installer/releases/tag/v0_0.9.27)
* Run `winrun.ps` in the `windows` folder.
WSL (For full toolchain and image build:
* You will need to remove all spaces from your path i.e. `export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Windows/system32:/snap/bin` and continue the instructions. P.S. What in the world was Windows thinking, putting a space between "Program" and "Files"??!?
Linux instructions (both):
* Clone this repo.
* Install `git build-essential` and/or whatever other requirements are in place for [buildroot](https://buildroot.org/).
* `make testdlimage`
* It automatically downloads the image (~1MB) and runs the emulator.
* Should be up and running in about 2.5s depending on internet speed.
You can do in-depth work on Linux by:
* `make everything`
If you want to play with the bare metal system, see below, or if you have the toolchain installed, just:
* `make testbare`
If you just want to play emdoom, and use the prebuilt image:
* On Windows, run `windows\winrundoom.ps1`
* On Linux, `cd mini-rv32ima`, and type `make testdoom`
## Questions?
* Why not rv64?
* Because then I can't run it as easily in a pixel shader if I ever hope to.
* Can I add an MMU?
* Yes. It actually probably wouldn't be too difficult.
* Should I add an MMU?
* No. It is important to further support for nommu systems to empower minimal Risc-V designs!
Everything else: Contact us on my Discord: https://discord.com/invite/CCeyWyZ
## How do I use this in my own project?
You shoud not need to modify `mini-rv32ima.h`, but instead, use `mini-rv32ima.c` as a template for what you are trying to do in your own project.
You can override all functionality by defining the following macros. Here are examples of what `mini-rv32ima.c` does with them. You can see the definition of the functions, or augment their definitions, by altering `mini-rv32ima.c`.
| Macro | Definition / Comment |
| --- | --- |
| `MINIRV32WARN( x... )` | `printf( x );` <br> Warnings emitted from mini-rv32ima.h |
| `MINIRV32_DECORATE` | `static` <br> How to decorate the functions. |
| `MINI_RV32_RAM_SIZE` | `ram_amt` <br> A variable, how big is system RAM? |
| `MINIRV32_IMPLEMENTATION` | If using mini-rv32ima.h, need to define this. |
| `MINIRV32_POSTEXEC( pc, ir, retval )` | `{ if( retval > 0 ) { if( fail_on_all_faults ) { printf( "FAULT\n" ); return 3; } else retval = HandleException( ir, retval ); } }` <br> If you want to execute something every time slice. |
| `MINIRV32_HANDLE_MEM_STORE_CONTROL( addy, val )` | `if( HandleControlStore( addy, val ) ) return val;` <br> Called on non-RAM memory access. |
| `MINIRV32_HANDLE_MEM_LOAD_CONTROL( addy, rval )` | `rval = HandleControlLoad( addy );` <br> Called on non-RAM memory access return a value. |
| `MINIRV32_OTHERCSR_WRITE( csrno, value )` | `HandleOtherCSRWrite( image, csrno, value );` <br> You can use CSRs for control requests. |
| `MINIRV32_OTHERCSR_READ( csrno, value )` | `value = HandleOtherCSRRead( image, csrno );` <br> You can use CSRs for control requests. |
## Hopeful goals?
* Further drive down needed features to run Linux.
* Remove need for RV32A extension on systems with only one CPU.
* Support for relocatable ELF executables.
* Add support for an unreal UART. One that's **much** simpler than the current 8250 driver.
* Maybe run this in a pixelshader too!
* Get opensbi working with this.
* Be able to "embed" rv32 emulators in random projects.
* Can I use early console to be a full system console?
* Can I increase the maximum contiguous memory allocatable?
## Special Thanks
* For @regymm and their [patches to buildroot](https://github.com/regymm/buildroot) and help!
* callout: Regymm's [quazisoc project](https://github.com/regymm/quasiSoC/).
* Buildroot (For being so helpful).
* @vowstar and their team working on [k210-linux-nommu](https://github.com/vowstar/k210-linux-nommu).
* This [guide](https://jborza.com/emulation/2020/04/09/riscv-environment.html)
* [rvcodecjs](https://luplab.gitlab.io/rvcodecjs/) I probably went through over 1,000 codes here.
* @splinedrive from the [KianV RISC-V noMMU SoC](https://github.com/splinedrive/kianRiscV/tree/master/linux_socs/kianv_harris_mcycle_edition?s=09) project.
## More details
If you want to build the kernel yourself:
* `make everything`
* About 20 minutes. (Or 4+ hours if you're on [Windows Subsytem for Linux 2](https://github.com/microsoft/WSL/issues/4197))
* And you should be dropped into a Linux busybox shell with some little tools that were compiled here.
## Emdoom notes
* Emdoom building is in the `experiments/emdoom` folder
* You *MUST* build your kernel with `MAX_ORDER` set to >12 in `buildroot/output/build/linux-5.19/include/linux/mmzone.h` if you are building your own image.
* You CAN use the pre-existing image that is described above.
* On Windows, it will be very slow. Not sure why.
If you want to use bare metal to build your binaries so you don't need buildroot, you can use the rv64 gcc in 32-bit mode built into Ubuntu 20.04 and up.
```
sudo apt-get install gcc-multilib gcc-riscv64-unknown-elf make
```
## Links
* "Hackaday Supercon 2022: Charles Lohr - Assembly in 2022: Yes! We Still Use it and Here's Why" : https://www.youtube.com/watch?v=Gelf0AyVGy4
## Attic
## General notes:
* https://github.com/cnlohr/riscv_emufun/commit/2f09cdeb378dc0215c07eb63f5a6fb43dbbf1871#diff-b48ccd795ae9aced07d022bf010bf9376232c4d78210c3113d90a8d349c59b3dL440
(These things don't currently work)
### Building Tests
(This does not work, now)
```
cd riscv-tests
export CROSS_COMPILE=riscv64-linux-gnu-
export PLATFORM_RISCV_XLEN=32
CC=riscv64-linux-gnu-gcc ./configure
make XLEN=32 RISCV_PREFIX=riscv64-unknown-elf- RISCV_GCC_OPTS="-g -O1 -march=rv32imaf -mabi=ilp32f -I/usr/include"
```
### Building OpenSBI
(This does not currently work!)
```
cd opensbi
export CROSS_COMPILE=riscv64-unknown-elf-
export PLATFORM_RISCV_XLEN=32
make
```
### Extra links
* Clear outline of CSRs: https://five-embeddev.com/riscv-isa-manual/latest/priv-csrs.html
* Fonts used in videos: https://audiolink.dev/
### Using custom build
Where yminpatch is the patch from the mailing list.
```
rm -rf buildroot
git clone git://git.buildroot.net/buildroot
cd buildroot
git am < ../yminpatch.txt
make qemu_riscv32_nommu_virt_defconfig
make
# Or use our configs.
```
Note: For emdoom you will need to modify include/linux/mmzone.h and change MAX_ORDER to 13.
### Buildroot Notes
Add this:
https://github.com/cnlohr/buildroot/pull/1/commits/bc890f74354e7e2f2b1cf7715f6ef334ff6ed1b2
Use this:
https://github.com/cnlohr/buildroot/commit/e97714621bfae535d947817e98956b112eb80a75

View File

@ -1,143 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
struct MiniRV32IMAState;
void ecall_handler(struct MiniRV32IMAState *state);
#define ECALL_HANDLER(state) ecall_handler(state)
#define MINIRV32WARN( x... ) printf( x );
#define MINIRV32_DECORATE static
#define MINI_RV32_RAM_SIZE (32 * 1024 * 1024)
#define MINIRV32_IMPLEMENTATION
#define MINIRV32_RAM_IMAGE_OFFSET 0x0
#include "mini-rv32ima.h"
#define SYSCALL(num) (1025 + num)
void ecall_handler(struct MiniRV32IMAState *state) {
uint32_t a0 = REG(10);
uint32_t a1 = REG(11);
switch (state->regs[17]) // x17 | a7
{
case 1:
// PrintInt
printf("%d", a0);
break;
case 4:
// PrintString
printf("%s", a0);
break;
case 10:
fprintf(stderr, "\nexit: %d\n", a0);
exit(a0);
case 11:
// PrintChar
printf("%c", a0);
break;
case 93:
fprintf(stderr, "\nmain return code: %d\n", a0);
exit(a0);
case SYSCALL(0):
// getchar();
REGSET(10, getchar());
case SYSCALL(1):
// putchar
putchar(a0);
break;
case SYSCALL(4):
// input int
scanf("%d", &a0);
REGSET(10, a0);
break;
case SYSCALL(5):
// input string
scanf("%s", a0);
REGSET(10, a0);
break;
default:
MINIRV32WARN("Unhandled ECALL: %d\n", state->regs[17]);
exit(1);
break;
}
}
int main(int argc, char *argv[]) {
// gcc -DDEFAULT_FILE='\"flat.bin\"' .\ripes-vm.c -o rv32-vm.exe
struct MiniRV32IMAState state;
uint8_t *image = (uint8_t *)malloc(MINI_RV32_RAM_SIZE);
// 初始化状态
memset(&state, 0, sizeof(state));
state.pc = 0; // 程序计数器从0开始
state.mstatus = 0x80000000; // 设置机器模式
state.mtvec = 0x1000;
state.mie = 0x7; // 启用所有中断
// 初始化内存
memset(image, 0, MINI_RV32_RAM_SIZE);
#ifndef DEFAULT_FILE
#define DEFAULT_FILE "../ccompiler/backend/test_rv.bin"
#endif
const char* filename = DEFAULT_FILE;
// 加载 flatbin 文件
if (argc == 2) {
filename = argv[1];
}
FILE *file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "Usage: %s <flatbin_file>\n", argv[0]);
printf("Failed to open file %s\n", filename);
return 1;
}
fseek(file, 0, SEEK_END);
long flen = ftell(file);
fseek(file, 0, SEEK_SET);
if (flen > MINI_RV32_RAM_SIZE) {
fprintf(stderr, "Flatbin file is too large\n");
fclose(file);
return 1;
}
fread(image, flen, 1, file);
fclose(file);
// 运行模拟器
while (1) {
int32_t ret = MiniRV32IMAStep(&state, image, MINIRV32_RAM_IMAGE_OFFSET, 0, 1);
if (ret != 0) {
printf("Exception or interrupt occurred at PC: %d\n", state.pc);
return ret;
}
}
free(image);
return 0;
}
// static void DumpState( struct MiniRV32IMAState * core, uint8_t * ram_image )
// {
// uint32_t pc = core->pc;
// uint32_t pc_offset = pc - MINIRV32_RAM_IMAGE_OFFSET;
// uint32_t ir = 0;
// printf( "PC: %08x ", pc );
// if( pc_offset >= 0 && pc_offset < ram_amt - 3 )
// {
// ir = *((uint32_t*)(&((uint8_t*)ram_image)[pc_offset]));
// printf( "[0x%08x] ", ir );
// }
// else
// printf( "[xxxxxxxxxx] " );
// uint32_t * regs = core->regs;
// printf( "Z:%08x ra:%08x sp:%08x gp:%08x tp:%08x t0:%08x t1:%08x t2:%08x s0:%08x s1:%08x a0:%08x a1:%08x a2:%08x a3:%08x a4:%08x a5:%08x ",
// regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], regs[6], regs[7],
// regs[8], regs[9], regs[10], regs[11], regs[12], regs[13], regs[14], regs[15] );
// printf( "a6:%08x a7:%08x s2:%08x s3:%08x s4:%08x s5:%08x s6:%08x s7:%08x s8:%08x s9:%08x s10:%08x s11:%08x t3:%08x t4:%08x t5:%08x t6:%08x\n",
// regs[16], regs[17], regs[18], regs[19], regs[20], regs[21], regs[22], regs[23],
// regs[24], regs[25], regs[26], regs[27], regs[28], regs[29], regs[30], regs[31] );
// }

View File

@ -1,3 +0,0 @@
int main() {
return 65536;
}

View File

@ -1,8 +0,0 @@
int main() {
int a;
int b;
a = 1 + 2 * 3;
b = 7;
a = a - b + 1;
return a;
}

View File

@ -1,6 +0,0 @@
int main() {
int x = 10;
x = x + 1;
return x;
}

View File

@ -1,10 +0,0 @@
int main(void) {
int a;
a = 1;
if (a) {
a = 1;
} else {
a = 2;
}
return a;
}

View File

@ -1,10 +0,0 @@
int main(void) {
int a;
a = 0;
if (a) {
a = 1;
} else {
a = 2;
}
return a;
}

View File

@ -1,9 +0,0 @@
int add(int, int);
int main(void) {
return add(1, 2);
}
int add(int a, int b) {
return a + b;
}

View File

@ -1,5 +0,0 @@
int main() {
int i = 0;
while (i < 10) i = i + 1;
return i;
}

View File

@ -1,12 +0,0 @@
// #include <stdio.h>
int main() {
int i = 0;
int pow = 1;
do {
pow = pow * 2;
i = i + 1;
} while(i < 7);
// printf("%d", pow);
return pow;
}

View File

@ -1,7 +0,0 @@
int main() {
int num = 0;
for (int i = 0; i < 10; i += 1) {
num = num + 1;
}
return num;
}

View File

@ -1,7 +0,0 @@
int add(int a, int b) {
return a + b;
}
int main(void) {
return add(1, 2);
}

View File

@ -1,18 +0,0 @@
// #include <stdio.h>
int factorial(int num);
int main() {
int num = 5;
int result = factorial(num);
// printf("%d", result);
return result;
}
int factorial(int num) {
if (num == 0) {
return 1;
} else {
return num * factorial(num - 1);
}
}

View File

@ -1,30 +0,0 @@
# VM := ../../rv32-vm
# CC := ../../ccompiler
# STD_CC := gcc
# TESTS := $(wildcard *.c)
# # 定义所有测试目标
# TEST_TARGETS := $(patsubst %.c, %_test, $(TESTS))
# all: $(TEST_TARGETS)
# %_test: %.c
# @$(STD_CC) -g -o $@ $<
# @$(CC) $< flat.bin
# @./$@ ; ret_gcc=$$?
# @$(VM) flat.bin ; ret_vm=$$?
# @echo "Testing $@"
# @if [ $$ret_gcc -eq $$ret_vm ]; then \
# echo "$@ passed"; \
# else \
# echo "$@ failed: GCC returned $$ret_gcc, VM returned $$ret_vm"; \
# exit 1; \
# fi
# clean:
# rm -f $(TEST_TARGETS) flat.bin
# .PHONY: all clean
all:
python test.py

View File

@ -1,6 +0,0 @@
int main() {
int a, b;
a = 1;
b = 2;
return a + b;
}

View File

@ -1,86 +0,0 @@
import subprocess
import os
from pathlib import Path
# 配置参数
TEST_DIR = Path(".")
CC_PATH = Path("../../ccompiler.exe")
VM_PATH = Path("../../rv32-vm.exe")
WORKSPACE = Path(".") # 测试工作目录
# 测试用例映射表(示例)
TEST_CASE_MAP = {
"./01_return.c": 65536,
"./02_decl_expr.c": 1,
"./03_decl_init.c": 11,
"./04_if.c": 1,
"./05_else.c": 2,
"./06_fcall.c": 3,
"./07_while.c": 10,
"./08_do_while.c": 128,
"./09_for.c": 10,
"./10_main.c": 3,
"./11_recursive.c": 120,
}
def run_command(cmd, capture_stderr=True):
"""执行命令并捕获stderr"""
result = subprocess.run(
cmd,
cwd=WORKSPACE,
stderr=subprocess.PIPE if capture_stderr else None,
text=True,
timeout=1,
)
return result.stderr.strip() if capture_stderr else None
def run_test(test_file, expected):
print(f"\nTesting {test_file}...")
# 1. 编译生成flat.bin
compile_cmd = [str(CC_PATH), str(test_file)]
compile_err = run_command(compile_cmd)
if not (WORKSPACE / "flat.bin").exists():
print(f" Compilation failed: {compile_err}")
return False
# 2. 执行虚拟机
vm_cmd = [str(VM_PATH), "flat.bin"]
# 3. 解析返回值(假设最后一行是返回值)
try:
vm_err = run_command(vm_cmd)
actual = int(vm_err.split()[-1])
except (ValueError, IndexError) as e:
print(f" Invalid VM output: {vm_err}")
return False
except subprocess.TimeoutExpired:
print(" Timeout expired")
return False
# 4. 验证结果
if actual == expected:
print(f" PASSED {test_file}")
return True
else:
print(f" FAILED: Expected {expected}, got {actual}")
return False
def main():
passed = 0
total = 0
for test_file, expected in TEST_CASE_MAP.items():
total += 1
if run_test(TEST_DIR / test_file, expected):
passed += 1
# 清理中间文件
if (WORKSPACE / "flat.bin").exists():
os.remove(WORKSPACE / "flat.bin")
print(f"\nTest Summary: {passed}/{total} passed")
if __name__ == "__main__":
main()

11
ccompiler/ccompiler.c Normal file
View File

@ -0,0 +1,11 @@
#include "ccompiler.h"
asm_prog_t* smcc_cc(smcc_cc_t* cc) {
ast_node_t* root = cc_frontend(cc->file, cc->stream, cc->sread);
// TODO add config
ir_prog_t* prog = cc_middleend(root, &cc->midend_conf);
// TODO add config
asm_prog_t* asm_prog = cc_backend(prog, &cc->backend_conf);
}

21
ccompiler/ccompiler.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef __SMCC_CC_H__
#define __SMCC_CC_H__
// TODO
#include "frontend/frontend.h"
#include "middleend/middleend.h"
#include "backend/backend.h"
typedef struct smcc_cc {
const char *file;
void *stream;
sread_fn sread;
cc_midend_conf_t midend_conf;
cc_backend_conf_t backend_conf;
} smcc_cc_t;
typedef union asm_prog asm_prog_t;
asm_prog_t* smcc_cc(smcc_cc_t* cc);
#endif

View File

@ -7,7 +7,6 @@ CFLAGS = -g -Wall -I../..
LEXER_DIR = ./lexer LEXER_DIR = ./lexer
PARSER_DIR = ./parser PARSER_DIR = ./parser
AST_DIR = ./parser/ast AST_DIR = ./parser/ast
SYMTAB_DIR = ./parser/symtab
# 源文件列表 # 源文件列表
SRCS = \ SRCS = \
@ -23,9 +22,6 @@ SRCS = \
$(AST_DIR)/program.c \ $(AST_DIR)/program.c \
$(AST_DIR)/stmt.c \ $(AST_DIR)/stmt.c \
$(AST_DIR)/term.c \ $(AST_DIR)/term.c \
$(SYMTAB_DIR)/hashmap.c \
$(SYMTAB_DIR)/scope.c \
$(SYMTAB_DIR)/symtab.c \
# 生成目标文件列表 # 生成目标文件列表
OBJS = $(SRCS:.c=.o) OBJS = $(SRCS:.c=.o)

View File

@ -1,17 +1,18 @@
#include <lib/core.h> #include <lib/core.h>
#include "frontend.h" #include "frontend.h"
#include "parser/symtab/symtab.h"
ast_node_t* frontend(const char* file, void* stream, sread_fn sread) { ast_node_t* cc_frontend(const char* file, void* stream, sread_fn sread) {
init_lib_core(); init_lib_core();
strpool_t strpool; strpool_t strpool;
init_strpool(&strpool); init_strpool(&strpool);
lexer_t lexer; cc_lexer_t lexer;
init_lexer(&lexer, file, stream, sread, &strpool); init_lexer(&lexer, file, stream, sread, &strpool);
symtab_t symtab; symtab_t symtab;
init_symtab(&symtab); init_symtab(&symtab);
// TODO global scope
symtab_enter_scope(&symtab);
parser_t parser; parser_t parser;
init_parser(&parser, &lexer, &symtab); init_parser(&parser, &lexer, &symtab);

View File

@ -1,9 +1,9 @@
#ifndef __SMCC_FRONTEND_H__ #ifndef __SMCC_CC_FRONTEND_H__
#define __SMCC_FRONTEND_H__ #define __SMCC_CC_FRONTEND_H__
#include "lexer/lexer.h" #include "lexer/lexer.h"
#include "parser/parser.h" #include "parser/parser.h"
typedef int (*sread_fn)(void *dst_buf, int dst_size, int elem_size, int count, void *stream); typedef int (*sread_fn)(void *dst_buf, int elem_size, int count, void *stream);
ast_node_t* frontend(const char* file, void* stream, sread_fn sread); ast_node_t* cc_frontend(const char* file, void* stream, sread_fn sread);
#endif #endif

View File

@ -74,7 +74,7 @@ static inline int keyword_cmp(const char* name, int len) {
return -1; // Not a keyword. return -1; // Not a keyword.
} }
void init_lexer(lexer_t* lexer, const char* file_name, void* stream, lexer_sread_fn sread, strpool_t* strpool) { void init_lexer(cc_lexer_t* lexer, const char* file_name, void* stream, lexer_sread_fn sread, strpool_t* strpool) {
lexer->strpool = strpool; lexer->strpool = strpool;
lexer->cur_ptr = lexer->end_ptr = (char*)&(lexer->buffer); lexer->cur_ptr = lexer->end_ptr = (char*)&(lexer->buffer);
lexer->loc.fname = strpool_intern(lexer->strpool, file_name); lexer->loc.fname = strpool_intern(lexer->strpool, file_name);
@ -87,7 +87,7 @@ void init_lexer(lexer_t* lexer, const char* file_name, void* stream, lexer_sread
rt_memset(lexer->buffer, 0, sizeof(lexer->buffer)); rt_memset(lexer->buffer, 0, sizeof(lexer->buffer));
} }
static void flush_buffer(lexer_t* lexer) { static void flush_buffer(cc_lexer_t* lexer) {
int num = lexer->end_ptr - lexer->cur_ptr; int num = lexer->end_ptr - lexer->cur_ptr;
for (int i = 0; i < num; i++) { for (int i = 0; i < num; i++) {
lexer->buffer[i] = lexer->cur_ptr[i]; lexer->buffer[i] = lexer->cur_ptr[i];
@ -96,7 +96,7 @@ static void flush_buffer(lexer_t* lexer) {
int read_size = LEXER_BUFFER_SIZE - num; int read_size = LEXER_BUFFER_SIZE - num;
// TODO rt_size_t to int maybe lose precision // TODO rt_size_t to int maybe lose precision
int got_size = lexer->sread(lexer->buffer + num, read_size, 1, read_size, lexer->stream); int got_size = lexer->sread(lexer->buffer + num, 1, read_size, lexer->stream);
if (got_size < 0) { if (got_size < 0) {
LEX_ERROR("lexer read error"); LEX_ERROR("lexer read error");
} else if (got_size < read_size) { } else if (got_size < read_size) {
@ -110,7 +110,7 @@ static void flush_buffer(lexer_t* lexer) {
} }
} }
static void goto_newline(lexer_t* lexer) { static void goto_newline(cc_lexer_t* lexer) {
do { do {
if (lexer->cur_ptr == lexer->end_ptr) { if (lexer->cur_ptr == lexer->end_ptr) {
flush_buffer(lexer); flush_buffer(lexer);
@ -120,7 +120,7 @@ static void goto_newline(lexer_t* lexer) {
} while (*lexer->cur_ptr != '\n' && *lexer->cur_ptr != '\0'); } while (*lexer->cur_ptr != '\n' && *lexer->cur_ptr != '\0');
} }
static void goto_block_comment(lexer_t* lexer) { static void goto_block_comment(cc_lexer_t* lexer) {
while (1) { while (1) {
if (lexer->end_ptr - lexer->cur_ptr < 2) { if (lexer->end_ptr - lexer->cur_ptr < 2) {
flush_buffer(lexer); flush_buffer(lexer);
@ -159,7 +159,7 @@ static char got_slash(char* peek) {
return -1; return -1;
} }
static void parse_char_literal(lexer_t* lexer, tok_t* token) { static void parse_char_literal(cc_lexer_t* lexer, tok_t* token) {
char val = 0; char val = 0;
char* peek = lexer->cur_ptr + 1; char* peek = lexer->cur_ptr + 1;
if (*peek == '\\') { if (*peek == '\\') {
@ -175,7 +175,7 @@ static void parse_char_literal(lexer_t* lexer, tok_t* token) {
token->val.ch = val; token->val.ch = val;
} }
static void parse_string_literal(lexer_t* lexer, tok_t* token) { static void parse_string_literal(cc_lexer_t* lexer, tok_t* token) {
char* peek = lexer->cur_ptr + 1; char* peek = lexer->cur_ptr + 1;
// TODO string literal size check // TODO string literal size check
static char dest[LEXER_MAX_TOKEN_SIZE + 1]; static char dest[LEXER_MAX_TOKEN_SIZE + 1];
@ -200,7 +200,7 @@ static void parse_string_literal(lexer_t* lexer, tok_t* token) {
} }
// FIXME it write by AI maybe error // FIXME it write by AI maybe error
static void parse_number(lexer_t* lexer, tok_t* token) { static void parse_number(cc_lexer_t* lexer, tok_t* token) {
char* peek = lexer->cur_ptr; char* peek = lexer->cur_ptr;
int base = 10; int base = 10;
int is_float = 0; int is_float = 0;
@ -290,7 +290,7 @@ static void parse_number(lexer_t* lexer, tok_t* token) {
#define GOT_ONE_TOKEN_BUF_SIZE 64 #define GOT_ONE_TOKEN_BUF_SIZE 64
// /zh/c/language/operator_arithmetic.html // /zh/c/language/operator_arithmetic.html
void get_token(lexer_t* lexer, tok_t* token) { void get_token(cc_lexer_t* lexer, tok_t* token) {
// 需要保证缓冲区始终可读 // 需要保证缓冲区始终可读
if (lexer->end_ptr - lexer->cur_ptr < GOT_ONE_TOKEN_BUF_SIZE) { if (lexer->end_ptr - lexer->cur_ptr < GOT_ONE_TOKEN_BUF_SIZE) {
flush_buffer(lexer); flush_buffer(lexer);
@ -515,7 +515,7 @@ static const tok_basic_type_t tok_type_map[] = {
} }
// get_token maybe got invalid (with parser) // get_token maybe got invalid (with parser)
void get_valid_token(lexer_t* lexer, tok_t* token) { void get_valid_token(cc_lexer_t* lexer, tok_t* token) {
tok_basic_type_t type; tok_basic_type_t type;
do { do {
get_token(lexer, token); get_token(lexer, token);

View File

@ -10,10 +10,9 @@
#define LEXER_BUFFER_SIZE 4095 #define LEXER_BUFFER_SIZE 4095
#endif #endif
typedef int (*lexer_sread_fn)(void *dst_buf, int dst_size, typedef int (*lexer_sread_fn)(void *dst_buf, int elem_size, int count, void *stream);
int elem_size, int count, void *stream);
typedef struct lexer { typedef struct cc_lexer {
loc_t loc; loc_t loc;
char* cur_ptr; // 当前扫描的字符,但是还没有开始扫描 char* cur_ptr; // 当前扫描的字符,但是还没有开始扫描
@ -24,15 +23,15 @@ typedef struct lexer {
void* stream; void* stream;
strpool_t* strpool; strpool_t* strpool;
} lexer_t; } cc_lexer_t;
void init_lexer(lexer_t* lexer, const char* file_name, void* stream, void init_lexer(cc_lexer_t* lexer, const char* file_name, void* stream,
lexer_sread_fn sread, strpool_t* strpool); lexer_sread_fn sread, strpool_t* strpool);
// pure token getter it will included empty token like TOKEN_BLANK // pure token getter it will included empty token like TOKEN_BLANK
void get_token(lexer_t* lexer, tok_t* token); void get_token(cc_lexer_t* lexer, tok_t* token);
// get_token maybe got invalid (with parser as TOKEN_BLANK) // get_token maybe got invalid (with parser as TOKEN_BLANK)
void get_valid_token(lexer_t* lexer, tok_t* token); void get_valid_token(cc_lexer_t* lexer, tok_t* token);
#endif #endif

View File

@ -1,18 +0,0 @@
CC = gcc
CFLAGS = -g -Wall -I../../../.. -DLEX_LOG_LEVEL=4
SRC = ../lexer.c ../token.c
LIB = -L../../../../lib -lcore
all = test_all
test_all: test
./test
run:
$(CC) $(CFLAGS) $(SRC) run.c $(LIB) -o run
test:
$(CC) $(CFLAGS) $(SRC) $(LIB) -o test test.c
clean:
rm -f test run

View File

@ -1,56 +0,0 @@
#include "../lexer.h"
#include <stdio.h>
#include <string.h>
// gcc -g ../lexer.c ../token.c test_lexer.c -o test_lexer
/*
tok_tConstant {
int have;
union {
char ch;
int i;
float f;
double d;
long long ll;
char* str;
};
};
*/
int g_num;
int g_num_arr[3];
int main(int argc, char* argv[]) {
// int num = 0;
// You Must Be Call
init_lib_core();
if (argc == 3 && strcmp(argv[2], "-nodebug") == 0) {
log_set_level(NULL, LOG_LEVEL_ALL & ~LOG_LEVEL_DEBUG);
}
const char* file_name = "run.c";
if (argc == 2) {
file_name = argv[1];
}
FILE* fp = fopen(file_name, "r");
if (fp == NULL) {
perror("open file failed");
return 1;
}
printf("open file success\n");
lexer_t lexer;
strpool_t strpool;
init_strpool(&strpool);
init_lexer(&lexer, file_name, fp, (lexer_sread_fn)fread_s, &strpool);
tok_t tok;
while (1) {
get_valid_token(&lexer, &tok);
if (tok.sub_type == TOKEN_EOF) {
break;
}
LOG_DEBUG("tk type `%s` in %s:%d:%d", get_tok_name(tok.sub_type), tok.loc.fname, tok.loc.line, tok.loc.col);
// LOG_DEBUG("%s", tok.val.str);
// printf("line: %d, column: %d, type: %3d, typename: %s\n",
// lexer.line, lexer.index, tok.type, get_tok_name(tok.type));
}
}

View File

@ -1,172 +0,0 @@
// test_lexer.c
#include <lib/acutest.h>
#include "../lexer.h"
#include <string.h>
int test_read(void *dst_buf, int dst_size, int elem_size, int count, void *stream) {
if (stream == NULL) {
return 0;
}
int size = dst_size > elem_size * count ? elem_size * count : dst_size;
memcpy(dst_buf, stream, size);
return size;
}
// 测试辅助函数
static inline void test_lexer_string(const char* input, cc_tktype_t expected_type) {
lexer_t lexer;
tok_t token;
init_lexer(&lexer, "test.c", (void*)input, test_read);
get_valid_token(&lexer, &token);
TEST_CHECK(token.type == expected_type);
TEST_MSG("Expected: %s", get_tok_name(expected_type));
TEST_MSG("Got: %s", get_tok_name(token.type));
}
// 基础运算符测试
void test_operators() {
TEST_CASE("Arithmetic operators"); {
test_lexer_string("+", TOKEN_ADD);
test_lexer_string("++", TOKEN_ADD_ADD);
test_lexer_string("+=", TOKEN_ASSIGN_ADD);
test_lexer_string("-", TOKEN_SUB);
test_lexer_string("--", TOKEN_SUB_SUB);
test_lexer_string("-=", TOKEN_ASSIGN_SUB);
test_lexer_string("*", TOKEN_MUL);
test_lexer_string("*=", TOKEN_ASSIGN_MUL);
test_lexer_string("/", TOKEN_DIV);
test_lexer_string("/=", TOKEN_ASSIGN_DIV);
test_lexer_string("%", TOKEN_MOD);
test_lexer_string("%=", TOKEN_ASSIGN_MOD);
}
TEST_CASE("Bitwise operators"); {
test_lexer_string("&", TOKEN_AND);
test_lexer_string("&&", TOKEN_AND_AND);
test_lexer_string("&=", TOKEN_ASSIGN_AND);
test_lexer_string("|", TOKEN_OR);
test_lexer_string("||", TOKEN_OR_OR);
test_lexer_string("|=", TOKEN_ASSIGN_OR);
test_lexer_string("^", TOKEN_XOR);
test_lexer_string("^=", TOKEN_ASSIGN_XOR);
test_lexer_string("~", TOKEN_BIT_NOT);
test_lexer_string("<<", TOKEN_L_SH);
test_lexer_string("<<=", TOKEN_ASSIGN_L_SH);
test_lexer_string(">>", TOKEN_R_SH);
test_lexer_string(">>=", TOKEN_ASSIGN_R_SH);
}
TEST_CASE("Comparison operators"); {
test_lexer_string("==", TOKEN_EQ);
test_lexer_string("!=", TOKEN_NEQ);
test_lexer_string("<", TOKEN_LT);
test_lexer_string("<=", TOKEN_LE);
test_lexer_string(">", TOKEN_GT);
test_lexer_string(">=", TOKEN_GE);
}
TEST_CASE("Special symbols"); {
test_lexer_string("(", TOKEN_L_PAREN);
test_lexer_string(")", TOKEN_R_PAREN);
test_lexer_string("[", TOKEN_L_BRACKET);
test_lexer_string("]", TOKEN_R_BRACKET);
test_lexer_string("{", TOKEN_L_BRACE);
test_lexer_string("}", TOKEN_R_BRACE);
test_lexer_string(";", TOKEN_SEMICOLON);
test_lexer_string(",", TOKEN_COMMA);
test_lexer_string(":", TOKEN_COLON);
test_lexer_string(".", TOKEN_DOT);
test_lexer_string("...", TOKEN_ELLIPSIS);
test_lexer_string("->", TOKEN_DEREF);
test_lexer_string("?", TOKEN_COND);
}
}
// 关键字测试
void test_keywords() {
TEST_CASE("C89 keywords");
test_lexer_string("while", TOKEN_WHILE);
test_lexer_string("sizeof", TOKEN_SIZEOF);
// TEST_CASE("C99 keywords");
// test_lexer_string("restrict", TOKEN_RESTRICT);
// test_lexer_string("_Bool", TOKEN_INT); // 需确认你的类型定义
}
// 字面量测试
void test_literals() {
TEST_CASE("Integer literals"); {
// 十进制
test_lexer_string("0", TOKEN_INT_LITERAL);
test_lexer_string("123", TOKEN_INT_LITERAL);
// test_lexer_string("2147483647", TOKEN_INT_LITERAL);
// // 十六进制
// test_lexer_string("0x0", TOKEN_INT_LITERAL);
// test_lexer_string("0x1A3F", TOKEN_INT_LITERAL);
// test_lexer_string("0XABCDEF", TOKEN_INT_LITERAL);
// // 八进制
// test_lexer_string("0123", TOKEN_INT_LITERAL);
// test_lexer_string("0777", TOKEN_INT_LITERAL);
// // 边界值测试
// test_lexer_string("2147483647", TOKEN_INT_LITERAL); // INT_MAX
// test_lexer_string("4294967295", TOKEN_INT_LITERAL); // UINT_MAX
}
TEST_CASE("Character literals"); {
test_lexer_string("'a'", TOKEN_CHAR_LITERAL);
test_lexer_string("'\\n'", TOKEN_CHAR_LITERAL);
test_lexer_string("'\\t'", TOKEN_CHAR_LITERAL);
test_lexer_string("'\\\\'", TOKEN_CHAR_LITERAL);
test_lexer_string("'\\0'", TOKEN_CHAR_LITERAL);
}
TEST_CASE("String literals"); {
test_lexer_string("\"hello\"", TOKEN_STRING_LITERAL);
test_lexer_string("\"multi-line\\nstring\"", TOKEN_STRING_LITERAL);
test_lexer_string("\"escape\\\"quote\"", TOKEN_STRING_LITERAL);
}
// TEST_CASE("Floating literals");
// test_lexer_string("3.14e-5", TOKEN_FLOAT_LITERAL);
}
// 边界测试
void test_edge_cases() {
// TEST_CASE("Long identifiers");
// char long_id[LEXER_MAX_TOKEN_SIZE+2] = {0};
// memset(long_id, 'a', LEXER_MAX_TOKEN_SIZE+1);
// test_lexer_string(long_id, TOKEN_IDENT);
// TEST_CASE("Buffer boundary");
// char boundary[LEXER_BUFFER_SIZE*2] = {0};
// memset(boundary, '+', LEXER_BUFFER_SIZE*2-1);
// test_lexer_string(boundary, TOKEN_ADD);
}
// 错误处理测试
void test_error_handling() {
TEST_CASE("Invalid characters");
lexer_t lexer;
tok_t token;
init_lexer(&lexer, "test.c", NULL, test_read);
get_valid_token(&lexer, &token);
TEST_CHECK(token.type == TOKEN_EOF); // 应触发错误处理
}
// 测试列表
TEST_LIST = {
{"operators", test_operators},
{"keywords", test_keywords},
{"literals", test_literals},
{"edge_cases", test_edge_cases},
{"error_handling", test_error_handling},
{NULL, NULL}
};

View File

@ -89,6 +89,7 @@ typedef struct ast_node {
vector_header(children, struct ast_node *); vector_header(children, struct ast_node *);
} block; } block;
struct { struct {
symtab_key_t key;
struct ast_node * decl_node; struct ast_node * decl_node;
tok_t tok; tok_t tok;
} syms; } syms;

View File

@ -1,8 +1,6 @@
#include "../ast.h" #include "../ast.h"
#include "../parser.h" #include "../parser.h"
#include "../symtab/symtab.h"
#ifndef BLOCK_MAX_NODE #ifndef BLOCK_MAX_NODE
#define BLOCK_MAX_NODE (1024) #define BLOCK_MAX_NODE (1024)

View File

@ -1,6 +1,5 @@
#include "../ast.h" #include "../ast.h"
#include "../parser.h" #include "../parser.h"
#include "../symtab/symtab.h"
/** /**
* 0 false * 0 false
@ -49,7 +48,10 @@ ast_node_t* parse_decl_val(parser_t* parser) {
node->decl_val.type = type_node; node->decl_val.type = type_node;
node->decl_val.name = name_node; node->decl_val.name = name_node;
node->type = NT_DECL_VAR; node->type = NT_DECL_VAR;
symtab_add_symbol(parser->symtab, name_node->syms.tok.val.str, node, 0);
type_node->syms.key.uid = parser->symtab->cur_scope->uid;
type_node->syms.key.strp_name = name_node->syms.tok.val.str;
symtab_add(parser->symtab, &type_node->syms.key, node);
ttype = peek_tok_type(tokbuf); ttype = peek_tok_type(tokbuf);
if (ttype == TOKEN_ASSIGN) { if (ttype == TOKEN_ASSIGN) {

View File

@ -1,6 +1,5 @@
#include "../ast.h" #include "../ast.h"
#include "../parser.h" #include "../parser.h"
#include "../symtab/symtab.h"
// Copy from `CParse` // Copy from `CParse`
/** /**
@ -253,11 +252,10 @@ static ast_node_t* parse_call(tok_stream_t* tokbuf, symtab_t *symtab, ast_node_t
} }
pop_tok(tokbuf); // 跳过 ')' pop_tok(tokbuf); // 跳过 ')'
const char* name = ident->syms.tok.val.str; ast_node_t* sym = symtab_get(symtab, &ident->syms.key);
ast_node_t* sym = symtab_lookup_symbol(symtab, name);
// TODO check func is match // TODO check func is match
if (sym == NULL || sym->type != NT_DECL_FUNC) { if (sym == NULL || sym->type != NT_DECL_FUNC) {
LOG_ERROR("function not decl %s", name); LOG_FATAL("function not decl %s", ident->syms.key.strp_name);
} }
node->call.name = ident; node->call.name = ident;
node->call.func_decl = sym; node->call.func_decl = sym;
@ -345,10 +343,12 @@ static ast_node_t *parse_primary_expression(tok_stream_t* tokbuf, symtab_t *symt
case TOKEN_IDENT: case TOKEN_IDENT:
node = expect_pop_ident(tokbuf); node = expect_pop_ident(tokbuf);
cc_tktype_t ttype = peek_tok_type(tokbuf); cc_tktype_t ttype = peek_tok_type(tokbuf);
node->syms.key.uid = 0;
node->syms.key.strp_name = tok->val.str;
if (ttype == TOKEN_L_PAREN) { if (ttype == TOKEN_L_PAREN) {
node = parse_call(tokbuf, symtab, node); node = parse_call(tokbuf, symtab, node);
} else { } else {
void *sym = symtab_lookup_symbol(symtab, tok->val.str); void *sym = symtab_get(symtab, &node->syms.key);
if (sym == NULL) { if (sym == NULL) {
LOG_ERROR("undefined symbol but use %s", tok->val.str); LOG_ERROR("undefined symbol but use %s", tok->val.str);
} }

View File

@ -1,6 +1,5 @@
#include "../ast.h" #include "../ast.h"
#include "../parser.h" #include "../parser.h"
#include "../symtab/symtab.h"
#ifndef FUNC_PARAM_CACHE_SIZE #ifndef FUNC_PARAM_CACHE_SIZE
#define FUNC_PARAM_CACHE_SIZE 32 // 合理初始值可覆盖99%常见情况 #define FUNC_PARAM_CACHE_SIZE 32 // 合理初始值可覆盖99%常见情况
@ -40,7 +39,10 @@ static void parse_params(parser_t* parser, tok_stream_t* cache, ast_node_t* node
node->decl_val.expr_stmt = NULL; node->decl_val.expr_stmt = NULL;
node->decl_val.data = NULL; node->decl_val.data = NULL;
vector_push(params->params.params, node); vector_push(params->params.params, node);
symtab_add_symbol(parser->symtab, id_node->syms.tok.val.str, node, 0);
id_node->syms.key.uid = parser->symtab->cur_scope->uid;
id_node->syms.key.strp_name = id_node->syms.tok.val.str;
symtab_add(parser->symtab, &id_node->syms.key, node);
break; break;
case TOKEN_L_PAREN: { case TOKEN_L_PAREN: {
depth++; depth++;
@ -129,7 +131,11 @@ void parse_func(parser_t* parser) {
ast_type_t type = check_is_func_decl(&(parser->tokbuf), &cache); ast_type_t type = check_is_func_decl(&(parser->tokbuf), &cache);
ast_node_t* prev = symtab_add_symbol(parser->symtab, func_name, decl, 1);
name_node->syms.key.uid = parser->symtab->cur_scope->uid;
name_node->syms.key.strp_name = func_name;
ast_node_t* prev = symtab_get(parser->symtab, &name_node->syms.key);
// TODO Change something
if (prev != NULL) { if (prev != NULL) {
if (prev->type != NT_DECL_FUNC) { if (prev->type != NT_DECL_FUNC) {
LOG_ERROR("the symbol duplicate old is %d, new is func", prev->type); LOG_ERROR("the symbol duplicate old is %d, new is func", prev->type);
@ -143,6 +149,7 @@ void parse_func(parser_t* parser) {
} }
return; return;
} }
symtab_add(parser->symtab, &name_node->syms.key, decl);
vector_push(parser->root->root.children, decl); vector_push(parser->root->root.children, decl);
if (type == NT_DECL_FUNC) { if (type == NT_DECL_FUNC) {
return; return;

View File

@ -2,7 +2,7 @@
#include "parser.h" #include "parser.h"
#include "type.h" #include "type.h"
void init_parser(parser_t* parser, lexer_t* lexer, symtab_t* symtab) { void init_parser(parser_t* parser, cc_lexer_t* lexer, symtab_t* symtab) {
init_lib_core(); init_lib_core();
parser->cur_node = NULL; parser->cur_node = NULL;

View File

@ -2,23 +2,22 @@
#define __PARSER_H__ #define __PARSER_H__
#include "../lexer/lexer.h" #include "../lexer/lexer.h"
#include <lib/utils/symtab/symtab.h>
#include "ast.h" #include "ast.h"
typedef struct lexer lexer_t;
typedef struct symtab symtab_t;
#define PARSER_MAX_TOKEN_QUEUE 16 #define PARSER_MAX_TOKEN_QUEUE 16
typedef struct parser { typedef struct parser {
ast_node_t* root; ast_node_t* root;
ast_node_t* cur_node; ast_node_t* cur_node;
lexer_t* lexer; cc_lexer_t* lexer;
symtab_t* symtab; symtab_t* symtab;
tok_stream_t tokbuf; tok_stream_t tokbuf;
tok_t TokenBuffer[PARSER_MAX_TOKEN_QUEUE]; tok_t TokenBuffer[PARSER_MAX_TOKEN_QUEUE];
int err_level; int err_level;
} parser_t; } parser_t;
void init_parser(parser_t* parser, lexer_t* lexer, symtab_t* symtab); void init_parser(parser_t* parser, cc_lexer_t* lexer, symtab_t* symtab);
void run_parser(parser_t* parser); void run_parser(parser_t* parser);
#endif #endif

View File

@ -1,4 +0,0 @@
extern int _print_str(const char* str);
int main(void) {
_print_str("Hello, world!\n");
}

View File

@ -1,14 +0,0 @@
// int __print_str(char* str);
int f(void);
int main(void) {
int a;
// f();
// a = 1 + 2 * 3 + 4;
// __print_str("Hello, world!\n");
a = 3 - f() * (3 + 2) % 6;
// 测试用例:
// if (a) if (2) 3; else b;
// 是否正确解析为 if (a) { if (b) c else d }
}

View File

@ -1,37 +0,0 @@
#include "../parser.h"
#include "../ast/ast.h"
#include "../symtab/symtab.h"
#include <stdio.h>
// gcc -g ../parser.c ../../lexer/lexer.c ../ast/ast.c ../ast/block.c ../ast/decl.c ../ast/expr.c ../ast/func.c ../ast/program.c ../ast/stmt.c ../ast/term.c ../symtab/hashmap.c ../symtab/scope.c ../symtab/symtab.c test_parser.c -o test_parser
// gcc -g test_parser.c -L../.. -lfrontend -o test_parser
int main(int argc, char** argv) {
init_lib_core();
const char* file_name = "test_file.c";
if (argc == 2) {
file_name = argv[1];
}
FILE* fp = fopen(file_name, "r");
if (fp == NULL) {
perror("open file failed");
return 1;
}
printf("open file success\n");
lexer_t lexer;
strpool_t strpool;
init_strpool(&strpool);
init_lexer(&lexer, file_name, fp, (lexer_sread_fn)fread_s, &strpool);
struct SymbolTable symtab;
init_symtab(&symtab);
struct parser parser;
init_parser(&parser, &lexer, &symtab);
parse_prog(&parser);
printf("parse_end\n");
pnt_ast(parser.root, 0);
return 0;
}

View File

@ -58,6 +58,8 @@ static ir_node_t* gen_ir_term(ast_node_t* node) {
Panic("gen_ir_expr: unknown node type"); Panic("gen_ir_expr: unknown node type");
} }
} }
TODO();
return NULL;
} }
static ir_node_t* gen_ir_expr(ast_node_t* node) { static ir_node_t* gen_ir_expr(ast_node_t* node) {
@ -239,8 +241,11 @@ void gen_ir_jmp(ast_node_t* node) {
switch (node->type) { switch (node->type) {
case NT_STMT_IF: { case NT_STMT_IF: {
ir_bblock_t* trueb = bblocks[0]; ir_bblock_t* trueb = bblocks[0];
trueb->label = "if_true";
ir_bblock_t* falseb = bblocks[1]; ir_bblock_t* falseb = bblocks[1];
falseb->label = "if_false";
ir_bblock_t* endb = bblocks[2]; ir_bblock_t* endb = bblocks[2];
endb->label = "if_end";
ir_node_t* jmp; ir_node_t* jmp;
// cond // cond
@ -248,19 +253,16 @@ void gen_ir_jmp(ast_node_t* node) {
emit_br(cond, trueb, falseb); emit_br(cond, trueb, falseb);
// true block // true block
vector_push(ctx.cur_func->bblocks, trueb);
ctx.cur_block = trueb; ctx.cur_block = trueb;
_gen_ir_from_ast(node->if_stmt.if_stmt); _gen_ir_from_ast(node->if_stmt.if_stmt);
// else block // else block
if (node->if_stmt.else_stmt != NULL) { if (node->if_stmt.else_stmt != NULL) {
vector_push(ctx.cur_func->bblocks, falseb);
ctx.cur_block = falseb; ctx.cur_block = falseb;
_gen_ir_from_ast(node->if_stmt.else_stmt); _gen_ir_from_ast(node->if_stmt.else_stmt);
ir_node_t* jmp; ir_node_t* jmp;
ctx.cur_block = endb; ctx.cur_block = endb;
vector_push(ctx.cur_func->bblocks, ctx.cur_block);
NEW_IR_JMP(jmp, ctx.cur_block); NEW_IR_JMP(jmp, ctx.cur_block);
emit_instr(falseb, jmp); emit_instr(falseb, jmp);
} else { } else {

View File

@ -2,6 +2,7 @@
#define __IR_AST_H__ #define __IR_AST_H__
#include "ir.h" #include "ir.h"
typedef struct ast_node ast_node_t;
ir_prog_t* gen_ir_from_ast(ast_node_t* node); ir_prog_t* gen_ir_from_ast(ast_node_t* node);
#endif // #endif //

View File

@ -0,0 +1,5 @@
#include "middleend.h"
ir_prog_t* cc_middleend(ast_node_t* root, cc_midend_conf_t* conf) {
return gen_ir_from_ast(root);
}

View File

@ -1,7 +1,13 @@
#ifndef __SMCC_MIDDLEEND_H__ #ifndef __SMCC_CC_MIDDLEEND_H__
#define __SMCC_MIDDLEEND_H__ #define __SMCC_CC_MIDDLEEND_H__
#include "ir/ir.h" #include "ir/ir.h"
#include "ir/ir_ast.h" #include "ir/ir_ast.h"
typedef struct cc_midend_conf {
// cc_arch_t arch;
} cc_midend_conf_t;
// TODO add some feature to cc_middleend like optimization
ir_prog_t* cc_middleend(ast_node_t* root, cc_midend_conf_t* conf);
#endif // __SMCC_MIDDLEEND_H__ #endif // __SMCC_MIDDLEEND_H__

View File

@ -1,8 +0,0 @@
all: test_ir
test_ir: frontend
gcc -g ../ir.c test_ir.c -L../../frontend -lfrontend -o test_ir
frontend:
make -C ../../frontend

View File

@ -1,7 +0,0 @@
int add(int a, int b) {
return a + b;
}
int main(void) {
return add(1, 2);
}

View File

@ -1,18 +0,0 @@
#include "../ir.h"
#include "../../frontend/frontend.h"
int main(int argc, const char** argv) {
const char* file_name = "test_file.c";
if (argc == 2) {
file_name = argv[1];
}
FILE* fp = fopen(file_name, "r");
if (fp == NULL) {
perror("open file failed");
return 1;
}
printf("open file success\n");
struct ASTNode* root = frontend("test.c", fp, (sread_fn)fread_s);
gen_ir_from_ast(root);
return 0;
}

View File

@ -25,7 +25,7 @@ TOKBUF_DIR = $(UTILS_DIR)/tokbuf
SRCS += \ SRCS += \
$(DS_DIR)/hashtable.c \ $(DS_DIR)/hashtable.c \
$(STRPOOL_DIR)/strpool.c \ $(STRPOOL_DIR)/strpool.c \
# $(SYMTAB_DIR)/symtab.c \ $(SYMTAB_DIR)/symtab.c \
# $(TOKBUF_DIR)/tokbuf.c # $(TOKBUF_DIR)/tokbuf.c
# 生成目标文件列表 # 生成目标文件列表

View File

@ -21,7 +21,7 @@ extern rt_file_t rt_stderr;
typedef rt_file_t (*rt_fopen_t)(const char* file_name, const char* mode); typedef rt_file_t (*rt_fopen_t)(const char* file_name, const char* mode);
typedef int (*rt_fflush_t)(rt_file_t*file); typedef int (*rt_fflush_t)(rt_file_t*file);
typedef int (*rt_fclose_t)(rt_file_t file); typedef int (*rt_fclose_t)(rt_file_t file);
typedef int (*rt_freads_t)(void * dst_buf, rt_size_t dst_size, rt_size_t elem_size, rt_size_t count, rt_file_t file); typedef int (*rt_fread_t)(void * dst_buf, rt_size_t elem_size, rt_size_t count, rt_file_t file);
typedef int (*rt_fwrite_t)(const void * buf, rt_size_t size, rt_size_t count, rt_file_t file); typedef int (*rt_fwrite_t)(const void * buf, rt_size_t size, rt_size_t count, rt_file_t file);
typedef int (*rt_fprintf_t)(void * file, const char *format, ...); typedef int (*rt_fprintf_t)(void * file, const char *format, ...);
@ -36,7 +36,7 @@ typedef struct smcc_rt {
rt_fopen_t fopen; rt_fopen_t fopen;
rt_fflush_t fflush; rt_fflush_t fflush;
rt_fclose_t fclose; rt_fclose_t fclose;
rt_freads_t freads; rt_fread_t fread;
rt_fwrite_t fwrite; rt_fwrite_t fwrite;
// Optional useful runtime // Optional useful runtime
@ -47,18 +47,6 @@ typedef struct smcc_rt {
extern const smcc_rt_t rt; extern const smcc_rt_t rt;
// #ifndef NULL
// #ifdef __cplusplus
// #ifndef _WIN64
// #define NULL 0
// #else
// #define NULL 0LL
// #endif /* W64 */
// #else
// #define NULL ((void *)0)
// #endif
// #endif
#define NULL ((void *)0) #define NULL ((void *)0)
#endif #endif

View File

@ -13,7 +13,7 @@ const smcc_rt_t rt = {
.fopen = (rt_fopen_t)fopen, .fopen = (rt_fopen_t)fopen,
.fflush = (rt_fflush_t)fflush, .fflush = (rt_fflush_t)fflush,
.fclose = (rt_fclose_t)fclose, .fclose = (rt_fclose_t)fclose,
.freads = (rt_freads_t)fread_s, .fread = (rt_fread_t)fread,
.fwrite = (rt_fwrite_t)fwrite, .fwrite = (rt_fwrite_t)fwrite,
._realloc = (rt_realloc_t)realloc, ._realloc = (rt_realloc_t)realloc,

View File

@ -1,6 +1,7 @@
#ifndef __SMCC_RT_TYPE_H__ #ifndef __SMCC_RT_TYPE_H__
#define __SMCC_RT_TYPE_H__ #define __SMCC_RT_TYPE_H__
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
typedef int8_t i8_t; typedef int8_t i8_t;
@ -20,7 +21,7 @@ typedef intptr_t iptr_t;
typedef uintptr_t uptr_t; typedef uintptr_t uptr_t;
typedef size_t usz_t; typedef size_t usz_t;
typedef ssize_t isz_t; // typedef ssize_t isz_t;
// typedef u32_t uw_t; // typedef u32_t uw_t;
// typedef i32_t iw_t; // typedef i32_t iw_t;

View File

@ -2,11 +2,11 @@
#define INIT_HASH_TABLE_SIZE (32) #define INIT_HASH_TABLE_SIZE (32)
void hashtable_init(hash_table_t* ht) { void init_hashtable(hash_table_t* ht) {
vector_init(ht->entries); vector_init(ht->entries);
ht->count = 0; ht->count = 0;
ht->tombstone_count = 0; ht->tombstone_count = 0;
Assert(ht->key_cmp != NULL && ht->hash_func != NULL); // Assert(ht->key_cmp != NULL && ht->hash_func != NULL);
} }
static int next_power_of_two(int n) { static int next_power_of_two(int n) {
@ -127,3 +127,14 @@ void hashtable_destory(hash_table_t* ht) {
ht->count = 0; ht->count = 0;
ht->tombstone_count = 0; ht->tombstone_count = 0;
} }
void hashtable_foreach(hash_table_t* ht, hash_table_iter_func iter_func, void* context) {
for (rt_size_t i = 0; i < ht->entries.cap; i++) {
hash_entry_t* entry = &vector_at(ht->entries, i);
if (entry->state == ENTRY_ACTIVE) {
if (!iter_func(entry->key, entry->value, context)) {
break; // enable callback function terminal the iter
}
}
}
}

View File

@ -1,6 +1,7 @@
#ifndef __SMCC_HASHTABLE_H__ #ifndef __SMCC_HASHTABLE_H__
#define __SMCC_HASHTABLE_H__ #define __SMCC_HASHTABLE_H__
#include <lib/core.h>
#include <lib/rt/rt_alloc.h> #include <lib/rt/rt_alloc.h>
#include "vector.h" #include "vector.h"
@ -29,11 +30,14 @@ typedef struct hash_table {
} hash_table_t; } hash_table_t;
// WARN you need set hash_func and key_cmp before use // WARN you need set hash_func and key_cmp before use
void hashtable_init(hash_table_t* ht) ; void init_hashtable(hash_table_t* ht) ;
void* hashtable_set(hash_table_t* ht, const void* key, void* value); void* hashtable_set(hash_table_t* ht, const void* key, void* value);
void* hashtable_get(hash_table_t* ht, const void* key); void* hashtable_get(hash_table_t* ht, const void* key);
void* hashtable_get(hash_table_t* ht, const void* key); void* hashtable_del(hash_table_t* ht, const void* key);
void hashtable_destory(hash_table_t* ht); void hashtable_destory(hash_table_t* ht);
typedef int (*hash_table_iter_func)(const void* key, void* value, void* context);
void hashtable_foreach(hash_table_t* ht, hash_table_iter_func iter_func, void* context);
#endif // __SMCC_HASHTABLE_H__ #endif // __SMCC_HASHTABLE_H__

View File

@ -15,7 +15,7 @@
do { \ do { \
(vec).size = 0, \ (vec).size = 0, \
(vec).cap = 0, \ (vec).cap = 0, \
(vec).data = NULL; \ (vec).data = 0; \
} while(0) } while(0)
#define vector_push(vec, value) \ #define vector_push(vec, value) \

View File

@ -5,7 +5,7 @@ void init_strpool(strpool_t* pool) {
pool->ht.hash_func = (u32_t(*)(const void*))rt_strhash; pool->ht.hash_func = (u32_t(*)(const void*))rt_strhash;
pool->ht.key_cmp = (int(*)(const void*, const void*))rt_strcmp; pool->ht.key_cmp = (int(*)(const void*, const void*))rt_strcmp;
hashtable_init(&pool->ht); init_hashtable(&pool->ht);
} }
const char* strpool_intern(strpool_t* pool, const char* str) { const char* strpool_intern(strpool_t* pool, const char* str) {

View File

@ -0,0 +1,62 @@
#include "symtab.h"
static u32_t hash_func(const void* _key) {
const symtab_key_t* key = (symtab_key_t*)_key;
return rt_strhash(key->strp_name);
}
static int key_cmp(const void* _key1, const void* _key2) {
const symtab_key_t* key1 = (symtab_key_t*)_key1;
const symtab_key_t* key2 = (symtab_key_t*)_key2;
if (rt_strcmp(key1->strp_name, key2->strp_name) == 0) {
return 0;
}
return 1;
}
void init_symtab(symtab_t* symtab) {
symtab->cur_scope = NULL;
symtab->gid = 1;
init_hashtable(&symtab->global_table);
symtab->global_table.hash_func = hash_func;
symtab->global_table.key_cmp = key_cmp;
init_hashtable(&symtab->local_table);
symtab->local_table.hash_func = hash_func;
symtab->local_table.key_cmp = key_cmp;
}
void symtab_destroy(symtab_t* symtab) {
// TODO
}
void symtab_enter_scope(symtab_t* symtab) {
scope_t *scope = (scope_t*)salloc_alloc(sizeof(scope_t));
scope->parent = symtab->cur_scope;
scope->uid = symtab->gid++;
init_hashtable(&scope->table);
scope->table.hash_func = hash_func;
scope->table.key_cmp = key_cmp;
symtab->cur_scope = scope;
}
void symtab_leave_scope(symtab_t* symtab) {
Assert(symtab->cur_scope != NULL);
scope_t *parent = symtab->cur_scope->parent;
hashtable_destory(&symtab->cur_scope->table);
salloc_free(symtab->cur_scope);
symtab->cur_scope = parent;
}
void* symtab_get(symtab_t* symtab, symtab_key_t* key) {
for (scope_t* scope = symtab->cur_scope; scope != NULL; scope = scope->parent) {
void* val = hashtable_get(&scope->table, key);
if (val != NULL) {
return val;
}
}
return NULL;
}
void* symtab_add(symtab_t* symtab, symtab_key_t* key, void* val) {
return hashtable_set(&symtab->cur_scope->table, key, val);
}

View File

@ -1,6 +1,39 @@
#ifndef __SMCC_SYMTABL_H__ #ifndef __SMCC_SYMTABL_H__
#define __SMCC_SYMTABL_H__ #define __SMCC_SYMTABL_H__
#include <lib/core.h>
#include <lib/utils/ds/hashtable.h>
#include <lib/utils/strpool/strpool.h>
// FIXME 架构上可能有更好的方式解决
typedef struct symtab_key {
const char* strp_name;
int uid;
} symtab_key_t;
typedef struct scope {
int uid;
struct scope* parent;
hash_table_t table;
} scope_t;
typedef struct symtab {
hash_table_t global_table;
hash_table_t local_table;
scope_t* cur_scope;
int gid; // global id for generating unique scope id
} symtab_t;
void init_symtab(symtab_t* symtab);
void symtab_destroy(symtab_t* symtab);
void symtab_enter_scope(symtab_t* symtab);
void symtab_leave_scope(symtab_t* symtab);
void* symtab_get(symtab_t* symtab, symtab_key_t* key);
// WARNING key and val need you save, especially val
void* symtab_add(symtab_t* symtab, symtab_key_t* key, void* val);
#endif #endif

21
linker/Makefile Normal file
View File

@ -0,0 +1,21 @@
CC = gcc
CFLAGS = -g -Wall -I..
# CLFAGS += -fsanitize=address
all: smcc
smcc: cc asm lib
$(CC) $(CFLAGS) riscv32_crt.c riscv32_linker.c test_main.c -L../assembler/riscv32 -lasm -L../ccompiler -lcc -L../lib -lcore -o smcc
lib:
make -C ../lib
asm:
make -C ../assembler/riscv32
cc:
make -C ../ccompiler
clean:
make -C ../lib clean
make -C ../assembler/riscv32 clean
make -C ../ccompiler clean

6
linker/header.h Normal file
View File

@ -0,0 +1,6 @@
#include <assembler/riscv32/riscv32.h>
#include <assembler/riscv32/riscv32_instr.h>
#include <lib/core.h>
rv32_prog_t* gen_rv32_crt();
rv32_prog_t* link_rv32_prog(rv32_prog_t* progs[]);

21
linker/riscv32_crt.c Normal file
View File

@ -0,0 +1,21 @@
#include "header.h"
// #include <assembler/riscv32/riscv32.c>
rv32_prog_t* gen_rv32_crt() {
rv32_prog_t* crt = (rv32_prog_t*)salloc_alloc(sizeof(rv32_prog_t));
init_rv32_prog(crt, NULL);
rv32_li(crt, REG_SP, 0x1000);
rv32_li(crt, REG_RA, 0x0);
// call main
symasm_entry_t entry = {
.name = "main",
.attr = GLOBAL
};
rv32_call_l(crt, &entry);
// ecall exit2
rv32_li(crt, REG_A7, 93);
rv32_ecall(crt);
return crt;
}

65
linker/riscv32_linker.c Normal file
View File

@ -0,0 +1,65 @@
#include "header.h"
static u32_t text_addr = 0;
#define FOREACH_PROGS(progs) for (rv32_prog_t **pprog = progs, *prog = *progs; \
*pprog != NULL; prog = *(++pprog))
int append(const void *key, void *value, void *context) {
symtab_asm_t* symtab = (symtab_asm_t*) context;
symasm_entry_t* entry = (symasm_entry_t*) key;
u32_t* addr = (u32_t*) value;
*addr += text_addr; // size to index
salloc_alloc(16);
if (entry->attr == GLOBAL) {
symtab_asm_put(symtab, entry, *addr);
}
return 1;
}
rv32_prog_t* link_rv32_prog(rv32_prog_t* progs[]) {
symtab_asm_t symtab;
init_symtab_asm(&symtab);
rv32_prog_t* exe = salloc_alloc(sizeof(rv32_prog_t));
init_rv32_prog(exe, NULL);
FOREACH_PROGS(progs) {
prog->text_base_address = text_addr;
text_addr += prog->text.size;
}
exe->text.data = salloc_alloc(text_addr * sizeof(u32_t));
exe->text.cap = text_addr;
exe->text.size = text_addr;
text_addr = 0;
FOREACH_PROGS(progs) {
rt_memcpy(exe->text.data + text_addr, prog->text.data, prog->text.size * sizeof(u32_t));
// FIXME
hashtable_foreach(&prog->symtab.symtab, append, &symtab);
text_addr += prog->text.size;
}
// find by self
FOREACH_PROGS(progs) {
for (int i = 0; i < prog->rinstrs.size; i++) {
u32_t* paddr;
u32_t addr;
paddr = symtab_asm_get(&prog->symtab, &prog->rinstrs.data[i].target);
if (paddr == NULL) {
paddr = symtab_asm_get(&symtab, &prog->rinstrs.data[i].target);
if (paddr == NULL) {
LOG_FATAL("linker: %s not found", prog->rinstrs.data[i].target.name);
}
addr = *paddr;
} else {
addr = *paddr;
// addr += prog->text_base_address;
}
// FIXME
int cur_idx = prog->text_base_address + prog->rinstrs.data[i].address;
prog->rinstrs.data[i].instr.imm += (addr - cur_idx) * sizeof(u32_t);
emit_rv32_instr(exe, &prog->rinstrs.data[i].instr, cur_idx, NULL);
// symtab_get(&prog->symtab, &prog->rinstrs.data[i].target);
}
}
return exe;
}

58
linker/test_main.c Normal file
View File

@ -0,0 +1,58 @@
#include "header.h"
#include <ccompiler/ccompiler.h>
#include <stdio.h>
int main(int argc, char** argv) {
// gcc rv32ima_codegen.c -o rv32gen.exe
init_lib_core();
log_set_level(NULL, LOG_LEVEL_ERROR | LOG_LEVEL_WARN | LOG_LEVEL_FATAL);
const char* infilename = "test.c";
const char* outfilename = "flat.bin";
if (argc >= 2) {
infilename = argv[1];
}
if (argc >= 3) {
outfilename = argv[2];
}
FILE* in = fopen(infilename, "r");
FILE* out = fopen(outfilename, "wb");
if (in == NULL || out == NULL) {
printf("Failed to open file\n");
return 1;
}
smcc_cc_t cc = {
.file = infilename,
.sread = (sread_fn)fread,
.stream = in,
};
asm_prog_t *prog = smcc_cc(&cc);
// gen_rv32_instr(&prog->rv32);
rv32_prog_t* rv32_crt = gen_rv32_crt();
rv32_prog_t* rv32_progs[3] = {
rv32_crt,
&prog->rv32,
NULL,
};
// rv32_progs[0] = rv32_crt;
// rv32_progs[1] = &prog->rv32;
// rv32_progs[2] = NULL;
rv32_prog_t* code = link_rv32_prog(rv32_progs);
// for (int i = 0; i < code->instrs.size; i++) {
// fwrite(&code->instrs.data[i].instr, 4, 1, out);
// fflush(out);
// }
// Flatbin fmt
fwrite(code->text.data, sizeof(u32_t), code->text.size, out);
// fwrite(code->data.data, sizeof(u8_t)code->text.size, 1, out);
fclose(in);
fclose(out);
return 0;
}