mirror of
https://github.com/Noratrieb/jompiler.git
synced 2026-01-14 14:05:01 +01:00
GLORY TO SHADOWING
This commit is contained in:
parent
c0a2c8d1aa
commit
96f2dcf8af
2 changed files with 116 additions and 21 deletions
130
index.js
130
index.js
|
|
@ -104,7 +104,8 @@ function lex(input) {
|
||||||
case "}":
|
case "}":
|
||||||
case ",":
|
case ",":
|
||||||
case ";":
|
case ";":
|
||||||
case "+": {
|
case "+":
|
||||||
|
case "=": {
|
||||||
tokens.push({
|
tokens.push({
|
||||||
kind: head,
|
kind: head,
|
||||||
span: i - 1,
|
span: i - 1,
|
||||||
|
|
@ -302,6 +303,25 @@ function parse(tokens) {
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case "int": {
|
||||||
|
const typeSpan = tok.next("ident").span;
|
||||||
|
const name = tok.next("ident");
|
||||||
|
|
||||||
|
tok.next("=");
|
||||||
|
|
||||||
|
let init;
|
||||||
|
if (tok.peek()?.kind !== ";") {
|
||||||
|
init = parseExpr(tok);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: "declaration",
|
||||||
|
type: "int",
|
||||||
|
name: name.ident,
|
||||||
|
span: typeSpan,
|
||||||
|
init,
|
||||||
|
};
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
// fallthrough
|
// fallthrough
|
||||||
}
|
}
|
||||||
|
|
@ -382,14 +402,20 @@ function lower(ast) {
|
||||||
x86-64 codegen.
|
x86-64 codegen.
|
||||||
|
|
||||||
strategy: every expression returns its result in rax.
|
strategy: every expression returns its result in rax.
|
||||||
|
|
||||||
|
useful:
|
||||||
|
- https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
|
||||||
|
- https://gitlab.com/x86-psABIs/x86-64-ABI
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function littleEndian16(number) {
|
function littleEndian16(number) {
|
||||||
assertDefined(number);
|
assertDefined(number);
|
||||||
|
assert(typeof number === "number");
|
||||||
return [number & 0xff, (number >> 8) & 0xff];
|
return [number & 0xff, (number >> 8) & 0xff];
|
||||||
}
|
}
|
||||||
function littleEndian32(number) {
|
function littleEndian32(number) {
|
||||||
assertDefined(number);
|
assertDefined(number);
|
||||||
|
assert(typeof number === "number");
|
||||||
return [
|
return [
|
||||||
number & 0xff,
|
number & 0xff,
|
||||||
(number >> 8) & 0xff,
|
(number >> 8) & 0xff,
|
||||||
|
|
@ -400,12 +426,14 @@ function lower(ast) {
|
||||||
function littleEndian64(number) {
|
function littleEndian64(number) {
|
||||||
assertDefined(number);
|
assertDefined(number);
|
||||||
assert(number <= 0xff_ff_ff_ff);
|
assert(number <= 0xff_ff_ff_ff);
|
||||||
|
assert(typeof number === "number");
|
||||||
return [...littleEndian32(number), 0, 0, 0, 0];
|
return [...littleEndian32(number), 0, 0, 0, 0];
|
||||||
}
|
}
|
||||||
function signedLittleEndian64(number) {
|
function signedLittleEndian64(number) {
|
||||||
assertDefined(number);
|
assertDefined(number);
|
||||||
assert(number <= 0xff_ff_ff_ff);
|
assert(number <= 0xff_ff_ff_ff);
|
||||||
assert(number >= -(0xff_ff_ff_ff + 1));
|
assert(number >= -(0xff_ff_ff_ff + 1));
|
||||||
|
assert(typeof number === "number");
|
||||||
|
|
||||||
const array = littleEndian64(number);
|
const array = littleEndian64(number);
|
||||||
const signBit = array[3] & 0b10000000;
|
const signBit = array[3] & 0b10000000;
|
||||||
|
|
@ -454,11 +482,11 @@ function lower(ast) {
|
||||||
const REG_DI = RM_DI;
|
const REG_DI = RM_DI;
|
||||||
|
|
||||||
const REG_IGNORED = 0;
|
const REG_IGNORED = 0;
|
||||||
function modRm(mod, rm, reg) {
|
function modRm(mod, reg, rm) {
|
||||||
assert(mod <= 0b11);
|
assert(mod <= 0b11);
|
||||||
assert(rm <= 0b111);
|
assert(rm <= 0b111);
|
||||||
assert(reg <= 0b111);
|
assert(reg <= 0b111);
|
||||||
return (mod << 6) | rm | (reg << 3);
|
return (mod << 6) | (reg << 3) | rm;
|
||||||
}
|
}
|
||||||
|
|
||||||
const REX = {
|
const REX = {
|
||||||
|
|
@ -477,7 +505,7 @@ function lower(ast) {
|
||||||
class InstBuilder {
|
class InstBuilder {
|
||||||
/**
|
/**
|
||||||
* The reserved stack space for locals and intermediary values.
|
* The reserved stack space for locals and intermediary values.
|
||||||
* todo todo something
|
* We subtract rsp by this value in the beginning.
|
||||||
*/
|
*/
|
||||||
#stackSize;
|
#stackSize;
|
||||||
#patches;
|
#patches;
|
||||||
|
|
@ -493,14 +521,14 @@ function lower(ast) {
|
||||||
#prologue() {
|
#prologue() {
|
||||||
// push rbp
|
// push rbp
|
||||||
this.pushReg64(REG_BP); // push british petroleum
|
this.pushReg64(REG_BP); // push british petroleum
|
||||||
|
// mov rbp, rsp
|
||||||
|
this.movRegReg64(REG_BP, REG_SP);
|
||||||
// sub rsp, SIZE
|
// sub rsp, SIZE
|
||||||
this.subImm(REG_SP, 0);
|
this.subImm(REG_SP, 0);
|
||||||
this.#patches.push({
|
this.#patches.push({
|
||||||
start: this.out.length - 4,
|
start: this.out.length - 4,
|
||||||
patch: () => littleEndian32(this.#stackSize),
|
patch: () => littleEndian32(this.#stackSize),
|
||||||
});
|
});
|
||||||
// mov rbp, rsp
|
|
||||||
this.movRegReg64(REG_BP, REG_SP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#epilogue() {
|
#epilogue() {
|
||||||
|
|
@ -522,22 +550,56 @@ function lower(ast) {
|
||||||
}
|
}
|
||||||
|
|
||||||
reserveStack(size) {
|
reserveStack(size) {
|
||||||
|
const prev = this.#stackSize;
|
||||||
this.#stackSize += size;
|
this.#stackSize += size;
|
||||||
return this.#stackSize;
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
addRegs(reg1, reg2) {
|
||||||
|
// REX.W + 03 /r | ADD r64, r/m64 ; Add r/m64 to r64
|
||||||
|
this.#append([
|
||||||
|
rex(REX.W_64_BIT_OPERAND_SIZE, 0, 0, 0),
|
||||||
|
0x03,
|
||||||
|
modRm(MOD_REG, reg1, reg2),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
movRegToStackOffset(offset, reg) {
|
||||||
|
// mov [rsp+{offset}], reg
|
||||||
|
// 89 /r, MOV r/m64, r64
|
||||||
|
this.#append([
|
||||||
|
0x89,
|
||||||
|
// [--][--]+disp32
|
||||||
|
modRm(0b10, reg, 0b100),
|
||||||
|
0x24, // SIB: Scaled index: none, Base: ESP
|
||||||
|
...littleEndian32(offset),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
movStackOffsetToReg(offset, reg) {
|
||||||
|
// mov reg, [rsp+{offset}]
|
||||||
|
// 89 /r, MOV r/m64, r64
|
||||||
|
this.#append([
|
||||||
|
0x8b,
|
||||||
|
// [--][--]+disp32
|
||||||
|
modRm(0b10, reg, 0b100),
|
||||||
|
0x24, // SIB: Scaled index: none, Base: ESP
|
||||||
|
...littleEndian32(offset),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
movEaxImm32(imm) {
|
movEaxImm32(imm) {
|
||||||
// mov eax, imm
|
// mov eax, imm
|
||||||
this.#append([
|
this.#append([
|
||||||
0xc7,
|
0xc7,
|
||||||
modRm(MOD_REG, RM_A, REG_IGNORED),
|
modRm(MOD_REG, REG_IGNORED, RM_A),
|
||||||
...littleEndian32(imm),
|
...littleEndian32(imm),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
movRegReg32(to, from) {
|
movRegReg32(to, from) {
|
||||||
// ; Move r/m32 to r32
|
// ; Move r/m32 to r32
|
||||||
this.#append([0x8b, modRm(MOD_REG, from, to)]);
|
this.#append([0x8b, modRm(MOD_REG, to, from)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
movRegReg64(to, from) {
|
movRegReg64(to, from) {
|
||||||
|
|
@ -545,7 +607,7 @@ function lower(ast) {
|
||||||
this.#append([
|
this.#append([
|
||||||
rex(REX.W_64_BIT_OPERAND_SIZE, 0, 0, 0),
|
rex(REX.W_64_BIT_OPERAND_SIZE, 0, 0, 0),
|
||||||
0x8b,
|
0x8b,
|
||||||
modRm(MOD_REG, from, to),
|
modRm(MOD_REG, to, from),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -564,7 +626,7 @@ function lower(ast) {
|
||||||
this.#append([
|
this.#append([
|
||||||
rex(REX.W_64_BIT_OPERAND_SIZE, 0, 0, 0),
|
rex(REX.W_64_BIT_OPERAND_SIZE, 0, 0, 0),
|
||||||
0x81,
|
0x81,
|
||||||
modRm(MOD_REG, reg, 5 /* /5*/),
|
modRm(MOD_REG, 5 /* /5*/, reg),
|
||||||
...littleEndian32(imm),
|
...littleEndian32(imm),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -593,7 +655,8 @@ function lower(ast) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function codegenExpr(ib, expr) {
|
function codegenExpr(ctx, expr) {
|
||||||
|
const { ib, variables } = ctx;
|
||||||
switch (expr.kind) {
|
switch (expr.kind) {
|
||||||
case "call": {
|
case "call": {
|
||||||
if (expr.lhs.kind !== "ident") {
|
if (expr.lhs.kind !== "ident") {
|
||||||
|
|
@ -604,7 +667,7 @@ function lower(ast) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: save
|
// TODO: save
|
||||||
codegenExpr(ib, expr.args[0]);
|
codegenExpr(ctx, expr.args[0]);
|
||||||
// mov edi, eax
|
// mov edi, eax
|
||||||
ib.movRegReg32(REG_DI, REG_A);
|
ib.movRegReg32(REG_DI, REG_A);
|
||||||
ib.call(expr.lhs.string);
|
ib.call(expr.lhs.string);
|
||||||
|
|
@ -615,9 +678,24 @@ function lower(ast) {
|
||||||
ib.movEaxImm32(expr.integer);
|
ib.movEaxImm32(expr.integer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "ident": {
|
||||||
|
const offset = [...variables]
|
||||||
|
.reverse()
|
||||||
|
.find((v) => v.name === expr.string);
|
||||||
|
assert(offset);
|
||||||
|
ib.movStackOffsetToReg(offset.stackOffset, REG_A);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "+": {
|
case "+": {
|
||||||
codegenExpr(ib, expr.rhs);
|
// For binary expressions, we first evaluate the LHS, save it on the stack,
|
||||||
assert(false);
|
// evaluate the RHS, then restore the LHS and perform the operation.
|
||||||
|
codegenExpr(ctx, expr.lhs);
|
||||||
|
ib.pushReg64(REG_A); // push rax
|
||||||
|
codegenExpr(ctx, expr.rhs);
|
||||||
|
ib.popReg64(REG_C); // pop rcx
|
||||||
|
|
||||||
|
ib.addRegs(REG_A, REG_C);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new Error(`unsupported expr: ${expr.kind}`);
|
throw new Error(`unsupported expr: ${expr.kind}`);
|
||||||
|
|
@ -627,21 +705,36 @@ function lower(ast) {
|
||||||
|
|
||||||
function codegenFunction(func) {
|
function codegenFunction(func) {
|
||||||
const ib = new InstBuilder();
|
const ib = new InstBuilder();
|
||||||
|
const variables = [];
|
||||||
|
|
||||||
for (const stmt of func.body) {
|
for (const stmt of func.body) {
|
||||||
switch (stmt.kind) {
|
switch (stmt.kind) {
|
||||||
case "expr": {
|
case "expr": {
|
||||||
codegenExpr(ib, stmt.expr);
|
codegenExpr({ ib, variables }, stmt.expr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "return": {
|
case "return": {
|
||||||
if (stmt.rhs) {
|
if (stmt.rhs) {
|
||||||
codegenExpr(ib, stmt.rhs);
|
codegenExpr({ ib, variables }, stmt.rhs);
|
||||||
}
|
}
|
||||||
ib.finish();
|
ib.finish();
|
||||||
ib.ret();
|
ib.ret();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "declaration": {
|
||||||
|
assert(stmt.type === "int");
|
||||||
|
if (stmt.init) {
|
||||||
|
codegenExpr({ ib, variables }, stmt.init);
|
||||||
|
}
|
||||||
|
const slot = ib.reserveStack(4);
|
||||||
|
variables.push({
|
||||||
|
name: stmt.name,
|
||||||
|
stackOffset: slot,
|
||||||
|
});
|
||||||
|
// mov [rsp+{slot}], eax
|
||||||
|
ib.movRegToStackOffset(slot, REG_A);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
@ -1040,7 +1133,8 @@ async function compile(input) {
|
||||||
const tokens = lex(input);
|
const tokens = lex(input);
|
||||||
console.log("tokens", tokens);
|
console.log("tokens", tokens);
|
||||||
const ast = parse(tokens);
|
const ast = parse(tokens);
|
||||||
console.dir("ast", ast, { depth: 20 });
|
console.log("ast");
|
||||||
|
console.dir(ast, { depth: 20 });
|
||||||
const object = lower(ast);
|
const object = lower(ast);
|
||||||
|
|
||||||
return link(object);
|
return link(object);
|
||||||
|
|
|
||||||
7
input.c
7
input.c
|
|
@ -2,11 +2,12 @@
|
||||||
|
|
||||||
int main(int argc)
|
int main(int argc)
|
||||||
{
|
{
|
||||||
thisismyfakeconstantbecauseidonthaveconstant(1);
|
int x = 100;
|
||||||
exit(42);
|
int x = 200;
|
||||||
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
int thisismyfakeconstantbecauseidonthaveconstant(int x)
|
int thisismyfakeconstantbecauseidonthaveconstant(int x)
|
||||||
{
|
{
|
||||||
// return 1 + 1;
|
return 1 + 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue