mirror of
				https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
				synced 2025-10-31 15:47:00 +01:00 
			
		
		
		
	works, but segfaults
This commit is contained in:
		
							parent
							
								
									2025a450f6
								
							
						
					
					
						commit
						fd2f6243a2
					
				
							
								
								
									
										14
									
								
								backup-compiler/.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								backup-compiler/.clang-format
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | Language: Cpp | ||||||
|  | BasedOnStyle: WebKit | ||||||
|  | IndentWidth: 4 | ||||||
|  | ColumnLimit: 80 | ||||||
|  | IndentCaseLabels: true | ||||||
|  | InsertNewlineAtEOF: true | ||||||
|  | AllowShortFunctionsOnASingleLine: None | ||||||
|  | 
 | ||||||
|  | BinPackArguments: false | ||||||
|  | AllowAllArgumentsOnNextLine: true | ||||||
|  | 
 | ||||||
|  | BinPackParameters: false | ||||||
|  | AllowAllParametersOfDeclarationOnNextLine: true | ||||||
|  | 
 | ||||||
							
								
								
									
										4
									
								
								backup-compiler/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								backup-compiler/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | out.nasm | ||||||
|  | *.o | ||||||
|  | out | ||||||
|  | 
 | ||||||
							
								
								
									
										18
									
								
								backup-compiler/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								backup-compiler/Makefile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | 
 | ||||||
|  | all: out | ||||||
|  | 
 | ||||||
|  | out: entry.o out.o lib.o | ||||||
|  | 	gcc $^ -o $@ -no-pie | ||||||
|  | 
 | ||||||
|  | %.o: %.c | ||||||
|  | 	gcc -c -o $@ -std=c17 -Wall -Wextra -Wpedantic -pedantic -pedantic-errors $^ | ||||||
|  | 
 | ||||||
|  | %.o: %.nasm | ||||||
|  | 	nasm -f elf64 $< -o $@ | ||||||
|  | 
 | ||||||
|  | out.nasm: program.sbl | ||||||
|  | 	deno run --allow-read --allow-write --check main.ts $< | ||||||
|  | 
 | ||||||
|  | clean: | ||||||
|  | 	rm -rf out.asm out.o lib.o entry.o out | ||||||
|  | 
 | ||||||
| @ -25,17 +25,28 @@ export class AsmGen { | |||||||
|         } |         } | ||||||
|         this.writeln(`section .text`); |         this.writeln(`section .text`); | ||||||
|         for (const fn of this.lir.fns) { |         for (const fn of this.lir.fns) { | ||||||
|             this.writeln(`align 8`); |             this.generateFn(fn); | ||||||
|             this.writeln(`global ${fn.label}:`); |  | ||||||
|             this.writeln(`${fn.label}:`); |  | ||||||
| 
 |  | ||||||
|             this.generateFnBody(fn); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         this.writeln(`; vim: syntax=nasm commentstring=;\\ %s`); | ||||||
|  |         this.writeln(""); | ||||||
|         return this.writer.finalize(); |         return this.writer.finalize(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private generateFnBody(fn: lir.Fn) { |     private generateFn(fn: lir.Fn) { | ||||||
|  |         const query = this.queryCFunction(fn); | ||||||
|  |         if (query.found) { | ||||||
|  |             const { label, args } = query; | ||||||
|  |             this.generateCFunctionBody(fn, label, args); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.generateFnBody(fn); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private queryCFunction( | ||||||
|  |         fn: lir.Fn, | ||||||
|  |     ): { found: false } | { found: true; label: string; args: number } { | ||||||
|         const stmtKind = fn.mir.stmt.kind; |         const stmtKind = fn.mir.stmt.kind; | ||||||
|         if (stmtKind.tag !== "fn") { |         if (stmtKind.tag !== "fn") { | ||||||
|             throw new Error(); |             throw new Error(); | ||||||
| @ -45,12 +56,41 @@ export class AsmGen { | |||||||
|             if (!arg || arg.kind.tag !== "string") { |             if (!arg || arg.kind.tag !== "string") { | ||||||
|                 throw new Error("incorrect args for attribute"); |                 throw new Error("incorrect args for attribute"); | ||||||
|             } |             } | ||||||
|             const label = arg.kind.val; |             return { | ||||||
|             this.generateCFunctionBody(label, fn.mir.paramLocals.size); |                 found: true, | ||||||
|             return; |                 label: arg.kind.val, | ||||||
|  |                 args: fn.mir.paramLocals.size, | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |         return { found: false }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private generateCFunctionBody(fn: lir.Fn, label: string, args: number) { | ||||||
|  |         this.writeln(`extern ${label}`); | ||||||
|  |         this.writeln(`global ${fn.label}`); | ||||||
|  |         this.writeln(`${fn.label}:`); | ||||||
|  | 
 | ||||||
|  |         this.writeIns(`push rbp`); | ||||||
|  |         this.writeIns(`mov rbp, rsp`); | ||||||
|  |         for (let i = 0; i < args; ++i) { | ||||||
|  |             this.writeIns(`mov rax, ${this.relative((i + 2) * 8)}`); | ||||||
|  |             this.writeIns(`push rax`); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.writeIns(`push r12`); |         const argRegs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"]; | ||||||
|  |         for (const reg of argRegs.slice(0, args + 1)) { | ||||||
|  |             this.writeIns(`pop ${reg}`); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.writeIns(`call ${label}`); | ||||||
|  |         this.writeIns(`mov rsp, rbp`); | ||||||
|  |         this.writeIns(`pop rbp`); | ||||||
|  |         this.writeIns(`ret`); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private generateFnBody(fn: lir.Fn) { | ||||||
|  |         this.writeln(`global ${fn.label}:`); | ||||||
|  |         this.writeln(`${fn.label}:`); | ||||||
|         this.writeIns(`push rbp`); |         this.writeIns(`push rbp`); | ||||||
|         this.writeIns(`mov rbp, rsp`); |         this.writeIns(`mov rbp, rsp`); | ||||||
|         this.writeIns(`sub rsp, ${fn.frameSize}`); |         this.writeIns(`sub rsp, ${fn.frameSize}`); | ||||||
| @ -64,30 +104,11 @@ export class AsmGen { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.writeln(`.exit:`); |         this.writeln(`.exit:`); | ||||||
|  |         const returnLocalOffset = fn.localOffsets.get(fn.mir.returnLocal.id)!; | ||||||
|  |         this.writeIns(`mov rax, QWORD ${this.relative(returnLocalOffset)}`); | ||||||
|         this.writeIns(`mov rsp, rbp`); |         this.writeIns(`mov rsp, rbp`); | ||||||
|         this.writeIns(`pop rbp`); |         this.writeIns(`pop rbp`); | ||||||
|         this.writeIns(`pop r12`); |         this.writeIns(`ret`); | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private generateCFunctionBody(label: string, args: number) { |  | ||||||
|         const argRegs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"]; |  | ||||||
|         const returnReg = "rax"; |  | ||||||
|         if (args > argRegs.length) { |  | ||||||
|             throw new Error( |  | ||||||
|                 `arg count (${args}) > ${argRegs.length} not supported`, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         this.writeIns(`push ${returnReg}`); |  | ||||||
|         for (const reg of argRegs.slice(0, args + 1)) { |  | ||||||
|             this.writeIns(`push ${reg}`); |  | ||||||
|         } |  | ||||||
|         this.writeIns(`call ${label}`); |  | ||||||
|         this.writeIns(`mov r12, rax`); |  | ||||||
|         for (const reg of argRegs.slice(0, args + 1).toReversed()) { |  | ||||||
|             this.writeIns(`push ${reg}`); |  | ||||||
|         } |  | ||||||
|         this.writeIns(`pop ${returnReg}`); |  | ||||||
|         this.writeIns(`push r12`); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private generateIns(ins: lir.Ins) { |     private generateIns(ins: lir.Ins) { | ||||||
| @ -115,16 +136,27 @@ export class AsmGen { | |||||||
|                 this.writeIns(`pop ${r(ins.reg)}`); |                 this.writeIns(`pop ${r(ins.reg)}`); | ||||||
|                 return; |                 return; | ||||||
|             case "load": |             case "load": | ||||||
|                 this.writeIns(`mov QWORD [rbp${ins.offset}], ${r(ins.reg)}`); |                 this.writeIns( | ||||||
|  |                     `mov ${r(ins.reg)}, QWORD ${this.relative(ins.offset)}`, | ||||||
|  |                 ); | ||||||
|                 return; |                 return; | ||||||
|             case "store": |             case "store_reg": | ||||||
|                 this.writeIns(`mov ${r(ins.reg)}, QWORD [rbp${ins.offset}]`); |                 this.writeIns( | ||||||
|  |                     `mov QWORD ${this.relative(ins.offset)}, ${r(ins.reg)}`, | ||||||
|  |                 ); | ||||||
|  |                 return; | ||||||
|  |             case "store_imm": | ||||||
|  |                 this.writeIns( | ||||||
|  |                     `mov QWORD ${this.relative(ins.offset)}, ${ins.val}`, | ||||||
|  |                 ); | ||||||
|                 return; |                 return; | ||||||
|             case "call_reg": |             case "call_reg": | ||||||
|                 this.generateCall(r(ins.reg), ins.args); |                 this.writeIns(`call ${r(ins.reg)}`); | ||||||
|  |                 this.writeIns(`push rax`); | ||||||
|                 return; |                 return; | ||||||
|             case "call_fn": |             case "call_imm": | ||||||
|                 this.generateCall(ins.fn.label, ins.args); |                 this.writeIns(`call ${ins.fn.label}`); | ||||||
|  |                 this.writeIns(`push rax`); | ||||||
|                 return; |                 return; | ||||||
|             case "jmp": |             case "jmp": | ||||||
|                 this.writeIns(`jmp .L${ins.target}`); |                 this.writeIns(`jmp .L${ins.target}`); | ||||||
| @ -153,27 +185,7 @@ export class AsmGen { | |||||||
|                 this.kill(ins.reg); |                 this.kill(ins.reg); | ||||||
|                 return; |                 return; | ||||||
|         } |         } | ||||||
|     } |         const _: never = ins; | ||||||
| 
 |  | ||||||
|     private generateCall(value: string, args: number) { |  | ||||||
|         const argRegs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"]; |  | ||||||
|         const returnReg = "rax"; |  | ||||||
|         if (args > argRegs.length) { |  | ||||||
|             throw new Error( |  | ||||||
|                 `arg count (${args}) > ${argRegs.length} not supported`, |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         this.writeIns(`push ${returnReg}`); |  | ||||||
|         for (const reg of argRegs.slice(0, args + 1)) { |  | ||||||
|             this.writeIns(`push ${reg}`); |  | ||||||
|         } |  | ||||||
|         this.writeIns(`call ${value}`); |  | ||||||
|         this.writeIns(`mov r12, rax`); |  | ||||||
|         for (const reg of argRegs.slice(0, args + 1).toReversed()) { |  | ||||||
|             this.writeIns(`push ${reg}`); |  | ||||||
|         } |  | ||||||
|         this.writeIns(`pop ${returnReg}`); |  | ||||||
|         this.writeIns(`push r12`); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private reg(reg: lir.Reg): string { |     private reg(reg: lir.Reg): string { | ||||||
| @ -198,7 +210,6 @@ export class AsmGen { | |||||||
| 
 | 
 | ||||||
|     private allocReg(reg: lir.Reg) { |     private allocReg(reg: lir.Reg) { | ||||||
|         if (!this.liveRegs.has(reg)) { |         if (!this.liveRegs.has(reg)) { | ||||||
|             this.liveRegs.add(reg); |  | ||||||
|             const regSel = [ |             const regSel = [ | ||||||
|                 "rax", |                 "rax", | ||||||
|                 "rdi", |                 "rdi", | ||||||
| @ -213,10 +224,15 @@ export class AsmGen { | |||||||
|             if (!regSel) { |             if (!regSel) { | ||||||
|                 throw new Error("ran out of registers"); |                 throw new Error("ran out of registers"); | ||||||
|             } |             } | ||||||
|  |             this.liveRegs.add(reg); | ||||||
|             this.regSelect.set(reg, regSel); |             this.regSelect.set(reg, regSel); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private relative(offset: number): string { | ||||||
|  |         return `[rbp${offset >= 0 ? `+${offset}` : `${offset}`}]`; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private kill(reg: lir.Reg) { |     private kill(reg: lir.Reg) { | ||||||
|         this.liveRegs.delete(reg); |         this.liveRegs.delete(reg); | ||||||
|         this.regSelect.delete(reg); |         this.regSelect.delete(reg); | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								backup-compiler/entry.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								backup-compiler/entry.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | extern int32_t sbc__main(void); | ||||||
|  | 
 | ||||||
|  | int main(void) | ||||||
|  | { | ||||||
|  |     sbc__main(); | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								backup-compiler/lib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								backup-compiler/lib.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | int64_t print_int(int64_t value) | ||||||
|  | { | ||||||
|  |     printf("%ld\n", value); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
| @ -11,6 +11,7 @@ export type Fn = { | |||||||
|     mir: mir.Fn; |     mir: mir.Fn; | ||||||
|     lines: Line[]; |     lines: Line[]; | ||||||
|     frameSize: number; |     frameSize: number; | ||||||
|  |     localOffsets: Map<number, number>; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type Line = { | export type Line = { | ||||||
| @ -27,9 +28,10 @@ export type Ins = | |||||||
|     | { tag: "push"; reg: Reg } |     | { tag: "push"; reg: Reg } | ||||||
|     | { tag: "pop"; reg: Reg } |     | { tag: "pop"; reg: Reg } | ||||||
|     | { tag: "load"; reg: Reg; offset: number } |     | { tag: "load"; reg: Reg; offset: number } | ||||||
|     | { tag: "store"; offset: number; reg: Reg } |     | { tag: "store_reg"; offset: number; reg: Reg } | ||||||
|  |     | { tag: "store_imm"; offset: number; val: number } | ||||||
|     | { tag: "call_reg"; reg: Reg; args: number } |     | { tag: "call_reg"; reg: Reg; args: number } | ||||||
|     | { tag: "call_fn"; fn: Fn; args: number } |     | { tag: "call_imm"; fn: Fn; args: number } | ||||||
|     | { tag: "jmp"; target: Label } |     | { tag: "jmp"; target: Label } | ||||||
|     | { tag: "jnz_reg"; reg: Reg; target: Label } |     | { tag: "jnz_reg"; reg: Reg; target: Label } | ||||||
|     | { tag: "ret" } |     | { tag: "ret" } | ||||||
| @ -52,7 +54,7 @@ export class ProgramStringifyer { | |||||||
|                         .map((label) => |                         .map((label) => | ||||||
|                             `${ |                             `${ | ||||||
|                                 label.labels |                                 label.labels | ||||||
|                                     .map((label) => `.${label}:\n`) |                                     .map((label) => `.L${label}:\n`) | ||||||
|                                     .join() |                                     .join() | ||||||
|                             }    ${this.ins(label.ins)}\n` |                             }    ${this.ins(label.ins)}\n` | ||||||
|                         ) |                         ) | ||||||
| @ -80,11 +82,13 @@ export class ProgramStringifyer { | |||||||
|                 return `pop %${ins.reg}`; |                 return `pop %${ins.reg}`; | ||||||
|             case "load": |             case "load": | ||||||
|                 return `load %${ins.reg}, ${ins.offset}`; |                 return `load %${ins.reg}, ${ins.offset}`; | ||||||
|             case "store": |             case "store_reg": | ||||||
|                 return `store ${ins.offset}, %${ins.reg}`; |                 return `store_reg ${ins.offset}, %${ins.reg}`; | ||||||
|  |             case "store_imm": | ||||||
|  |                 return `store_val ${ins.offset}, ${ins.val}`; | ||||||
|             case "call_reg": |             case "call_reg": | ||||||
|                 return `call_reg %${ins.reg}, ${ins.args}`; |                 return `call_reg %${ins.reg}, ${ins.args}`; | ||||||
|             case "call_fn": |             case "call_imm": | ||||||
|                 return `call_fn ${ins.fn.label}, ${ins.args}`; |                 return `call_fn ${ins.fn.label}, ${ins.args}`; | ||||||
|             case "jmp": |             case "jmp": | ||||||
|                 return `jmp .b${ins.target}`; |                 return `jmp .b${ins.target}`; | ||||||
|  | |||||||
| @ -31,7 +31,14 @@ export class LirGen { | |||||||
|             const mir = this.mirGen.fnMir(stmt, stmt.kind); |             const mir = this.mirGen.fnMir(stmt, stmt.kind); | ||||||
|             const id = this.fnIds++; |             const id = this.fnIds++; | ||||||
|             const label = `sbc__${stmt.kind.ident}`; |             const label = `sbc__${stmt.kind.ident}`; | ||||||
|             const fn: Fn = { id, label, mir, lines: [], frameSize: 0 }; |             const fn: Fn = { | ||||||
|  |                 id, | ||||||
|  |                 label, | ||||||
|  |                 mir, | ||||||
|  |                 lines: [], | ||||||
|  |                 frameSize: 0, | ||||||
|  |                 localOffsets: new Map(), | ||||||
|  |             }; | ||||||
|             this.fns.set(id, fn); |             this.fns.set(id, fn); | ||||||
|             this.stmtFns.set(stmt.id, fn); |             this.stmtFns.set(stmt.id, fn); | ||||||
|         } |         } | ||||||
| @ -69,8 +76,6 @@ class FnGen { | |||||||
| 
 | 
 | ||||||
|     private currentLabels: Label[] = []; |     private currentLabels: Label[] = []; | ||||||
| 
 | 
 | ||||||
|     private nextOffset = -8; |  | ||||||
|     private frameSize = 0; |  | ||||||
|     private localOffsets = new Map<number, number>(); |     private localOffsets = new Map<number, number>(); | ||||||
| 
 | 
 | ||||||
|     public constructor( |     public constructor( | ||||||
| @ -84,12 +89,33 @@ class FnGen { | |||||||
|             const label = this.labelIds++; |             const label = this.labelIds++; | ||||||
|             this.blockLabels.set(block.id, label); |             this.blockLabels.set(block.id, label); | ||||||
|         } |         } | ||||||
|         for (const local of this.fn.mir.locals) { | 
 | ||||||
|             this.localOffsets.set(local.id, this.nextOffset); |         let currentOffset = 8 + this.fn.mir.paramLocals.size * 8; | ||||||
|             this.nextOffset -= 8; |         let frameSize = 0; | ||||||
|             this.frameSize += 8; | 
 | ||||||
|  |         for (const local of this.fn.mir.paramLocals.values()) { | ||||||
|  |             this.localOffsets.set(local.id, currentOffset); | ||||||
|  |             currentOffset -= 8; | ||||||
|         } |         } | ||||||
|         this.fn.frameSize = this.frameSize; |         // return address
 | ||||||
|  |         currentOffset -= 8; | ||||||
|  |         // old rbp
 | ||||||
|  |         currentOffset -= 8; | ||||||
|  |         // return value
 | ||||||
|  |         this.localOffsets.set(this.fn.mir.returnLocal.id, currentOffset); | ||||||
|  |         currentOffset -= 8; | ||||||
|  |         frameSize += 8; | ||||||
|  |         for (const local of this.fn.mir.locals) { | ||||||
|  |             if (this.localOffsets.has(local.id)) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             this.localOffsets.set(local.id, currentOffset); | ||||||
|  |             currentOffset -= 8; | ||||||
|  |             frameSize += 8; | ||||||
|  |         } | ||||||
|  |         this.fn.frameSize = frameSize; | ||||||
|  |         this.fn.localOffsets = this.localOffsets; | ||||||
|  | 
 | ||||||
|         for (const block of this.fn.mir.blocks) { |         for (const block of this.fn.mir.blocks) { | ||||||
|             this.currentLabels.push(this.blockLabels.get(block.id)!); |             this.currentLabels.push(this.blockLabels.get(block.id)!); | ||||||
|             for (const stmt of block.stmts) { |             for (const stmt of block.stmts) { | ||||||
| @ -158,7 +184,7 @@ class FnGen { | |||||||
|                 const reg = this.reg(); |                 const reg = this.reg(); | ||||||
|                 const offset = this.localOffsets.get(k.local.id)!; |                 const offset = this.localOffsets.get(k.local.id)!; | ||||||
|                 this.pushIns({ tag: "pop", reg }); |                 this.pushIns({ tag: "pop", reg }); | ||||||
|                 this.pushIns({ tag: "store", offset, reg }); |                 this.pushIns({ tag: "store_reg", offset, reg }); | ||||||
|                 this.pushIns({ tag: "kill", reg }); |                 this.pushIns({ tag: "kill", reg }); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
| @ -175,8 +201,8 @@ class FnGen { | |||||||
|             case "mul": { |             case "mul": { | ||||||
|                 const dst = this.reg(); |                 const dst = this.reg(); | ||||||
|                 const src = this.reg(); |                 const src = this.reg(); | ||||||
|                 this.pushIns({ tag: "pop", reg: src }); |  | ||||||
|                 this.pushIns({ tag: "pop", reg: dst }); |                 this.pushIns({ tag: "pop", reg: dst }); | ||||||
|  |                 this.pushIns({ tag: "pop", reg: src }); | ||||||
|                 this.pushIns({ tag: k.tag, dst, src }); |                 this.pushIns({ tag: k.tag, dst, src }); | ||||||
|                 this.pushIns({ tag: "push", reg: dst }); |                 this.pushIns({ tag: "push", reg: dst }); | ||||||
|                 this.pushIns({ tag: "kill", reg: src }); |                 this.pushIns({ tag: "kill", reg: src }); | ||||||
|  | |||||||
							
								
								
									
										185
									
								
								backup-compiler/lir_optimize.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								backup-compiler/lir_optimize.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,185 @@ | |||||||
|  | import { | ||||||
|  |     Fn, | ||||||
|  |     Ins, | ||||||
|  |     Label, | ||||||
|  |     Line, | ||||||
|  |     Program, | ||||||
|  |     ProgramStringifyer, | ||||||
|  |     Reg, | ||||||
|  | } from "./lir.ts"; | ||||||
|  | 
 | ||||||
|  | export function lirOptimize(program: Program) { | ||||||
|  |     console.log("=== BEFORE OPTIMIZATION ==="); | ||||||
|  |     console.log(new ProgramStringifyer(program).stringify()); | ||||||
|  |     for (const fn of program.fns) { | ||||||
|  |         eliminatePushPop(fn); | ||||||
|  |         eliminateMovFnCall(fn); | ||||||
|  |         eliminateMovIntStoreReg(fn); | ||||||
|  |     } | ||||||
|  |     console.log("=== AFTER OPTIMIZATION ==="); | ||||||
|  |     console.log(new ProgramStringifyer(program).stringify()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function eliminatePushPop(fn: Fn) { | ||||||
|  |     const candidates: number[] = []; | ||||||
|  | 
 | ||||||
|  |     for (let i = 0; i < fn.lines.length - 2; ++i) { | ||||||
|  |         const [push, kill, pop] = fn.lines.slice(i); | ||||||
|  |         if ( | ||||||
|  |             push.ins.tag === "push" && | ||||||
|  |             kill.ins.tag === "kill" && | ||||||
|  |             pop.ins.tag === "pop" && | ||||||
|  |             kill.labels.length === 0 && | ||||||
|  |             pop.labels.length === 0 && | ||||||
|  |             push.ins.reg === kill.ins.reg | ||||||
|  |         ) { | ||||||
|  |             candidates.push(i); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (const i of candidates.toReversed()) { | ||||||
|  |         if (i + 3 >= fn.lines.length) { | ||||||
|  |             fn.lines[i + 3].labels.push(...fn.lines[i].labels); | ||||||
|  |         } | ||||||
|  |         const [push, kill, pop] = fn.lines.slice(i); | ||||||
|  |         if ( | ||||||
|  |             !( | ||||||
|  |                 push.ins.tag === "push" && | ||||||
|  |                 kill.ins.tag === "kill" && | ||||||
|  |                 pop.ins.tag === "pop" | ||||||
|  |             ) | ||||||
|  |         ) { | ||||||
|  |             throw new Error(); | ||||||
|  |         } | ||||||
|  |         const toRemove = pop.ins.reg; | ||||||
|  |         const replacement = push.ins.reg; | ||||||
|  |         fn.lines.splice(i, 3); | ||||||
|  |         replaceReg(fn, toRemove, replacement); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function eliminateMovFnCall(fn: Fn) { | ||||||
|  |     const candidates: number[] = []; | ||||||
|  | 
 | ||||||
|  |     for (let i = 0; i < fn.lines.length - 2; ++i) { | ||||||
|  |         const [movFn, callReg, kill] = fn.lines.slice(i); | ||||||
|  |         if ( | ||||||
|  |             movFn.ins.tag === "mov_fn" && | ||||||
|  |             callReg.ins.tag === "call_reg" && | ||||||
|  |             kill.ins.tag === "kill" && | ||||||
|  |             callReg.labels.length === 0 && | ||||||
|  |             kill.labels.length === 0 && | ||||||
|  |             movFn.ins.reg === callReg.ins.reg && | ||||||
|  |             movFn.ins.reg === kill.ins.reg | ||||||
|  |         ) { | ||||||
|  |             candidates.push(i); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (const i of candidates.toReversed()) { | ||||||
|  |         const [movFn, callReg, kill] = fn.lines.slice(i); | ||||||
|  |         if ( | ||||||
|  |             !( | ||||||
|  |                 movFn.ins.tag === "mov_fn" && | ||||||
|  |                 callReg.ins.tag === "call_reg" && | ||||||
|  |                 kill.ins.tag === "kill" | ||||||
|  |             ) | ||||||
|  |         ) { | ||||||
|  |             throw new Error(); | ||||||
|  |         } | ||||||
|  |         const fnVal = movFn.ins.fn; | ||||||
|  |         const args = callReg.ins.args; | ||||||
|  |         fn.lines.splice(i + 1, 2); | ||||||
|  |         fn.lines[i].ins = { tag: "call_imm", fn: fnVal, args }; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function eliminateMovIntStoreReg(fn: Fn) { | ||||||
|  |     const candidates: number[] = []; | ||||||
|  | 
 | ||||||
|  |     for (let i = 0; i < fn.lines.length - 2; ++i) { | ||||||
|  |         const [movInt, storeReg, kill] = fn.lines.slice(i); | ||||||
|  |         if ( | ||||||
|  |             movInt.ins.tag === "mov_int" && | ||||||
|  |             storeReg.ins.tag === "store_reg" && | ||||||
|  |             kill.ins.tag === "kill" && | ||||||
|  |             storeReg.labels.length === 0 && | ||||||
|  |             kill.labels.length === 0 && | ||||||
|  |             movInt.ins.reg === storeReg.ins.reg && | ||||||
|  |             movInt.ins.reg === kill.ins.reg | ||||||
|  |         ) { | ||||||
|  |             candidates.push(i); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (const i of candidates.toReversed()) { | ||||||
|  |         const [movInt, storeReg, kill] = fn.lines.slice(i); | ||||||
|  |         if ( | ||||||
|  |             !( | ||||||
|  |                 movInt.ins.tag === "mov_int" && | ||||||
|  |                 storeReg.ins.tag === "store_reg" && | ||||||
|  |                 kill.ins.tag === "kill" | ||||||
|  |             ) | ||||||
|  |         ) { | ||||||
|  |             throw new Error(); | ||||||
|  |         } | ||||||
|  |         const offset = storeReg.ins.offset; | ||||||
|  |         const val = movInt.ins.val; | ||||||
|  |         fn.lines.splice(i + 1, 2); | ||||||
|  |         fn.lines[i].ins = { tag: "store_imm", offset, val }; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function replaceReg(fn: Fn, cand: Reg, replacement: Reg) { | ||||||
|  |     const r = (reg: Reg): Reg => reg === cand ? replacement : reg; | ||||||
|  | 
 | ||||||
|  |     for (const { ins } of fn.lines) { | ||||||
|  |         switch (ins.tag) { | ||||||
|  |             case "error": | ||||||
|  |                 break; | ||||||
|  |             case "nop": | ||||||
|  |                 break; | ||||||
|  |             case "mov_int": | ||||||
|  |             case "mov_string": | ||||||
|  |             case "mov_fn": | ||||||
|  |                 ins.reg = r(ins.reg); | ||||||
|  |                 break; | ||||||
|  |             case "push": | ||||||
|  |             case "pop": | ||||||
|  |                 ins.reg = r(ins.reg); | ||||||
|  |                 break; | ||||||
|  |             case "load": | ||||||
|  |             case "store_reg": | ||||||
|  |                 ins.reg = r(ins.reg); | ||||||
|  |                 break; | ||||||
|  |             case "store_imm": | ||||||
|  |                 break; | ||||||
|  |             case "call_reg": | ||||||
|  |                 ins.reg = r(ins.reg); | ||||||
|  |                 break; | ||||||
|  |             case "call_imm": | ||||||
|  |                 break; | ||||||
|  |             case "jmp": | ||||||
|  |                 break; | ||||||
|  |             case "jnz_reg": | ||||||
|  |                 ins.reg = r(ins.reg); | ||||||
|  |                 break; | ||||||
|  |             case "ret": | ||||||
|  |                 break; | ||||||
|  |             case "lt": | ||||||
|  |             case "eq": | ||||||
|  |             case "add": | ||||||
|  |             case "mul": | ||||||
|  |                 ins.dst = r(ins.dst); | ||||||
|  |                 ins.src = r(ins.src); | ||||||
|  |                 break; | ||||||
|  |             case "kill": | ||||||
|  |                 ins.reg = r(ins.reg); | ||||||
|  |                 break; | ||||||
|  |             default: { | ||||||
|  |                 const _: never = ins; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| @ -5,35 +5,40 @@ import { FnStringifyer } from "./mir.ts"; | |||||||
| import { LirGen } from "./lir_gen.ts"; | import { LirGen } from "./lir_gen.ts"; | ||||||
| import { ProgramStringifyer } from "./lir.ts"; | import { ProgramStringifyer } from "./lir.ts"; | ||||||
| import { AsmGen } from "./asm_gen.ts"; | import { AsmGen } from "./asm_gen.ts"; | ||||||
|  | import { lirOptimize } from "./lir_optimize.ts"; | ||||||
| 
 | 
 | ||||||
| async function main() { | async function main() { | ||||||
|     const text = await Deno.readTextFile(Deno.args[0]); |     const text = await Deno.readTextFile(Deno.args[0]); | ||||||
| 
 | 
 | ||||||
|     const ast = new Parser(text).parse(); |     const ast = new Parser(text).parse(); | ||||||
|     console.log("=== AST ==="); |     // console.log("=== AST ===");
 | ||||||
|     console.log(yaml.stringify(ast)); |     // console.log(yaml.stringify(ast));
 | ||||||
| 
 | 
 | ||||||
|     const re = new Resolver(ast).resolve(); |     const re = new Resolver(ast).resolve(); | ||||||
|     const ch = new Checker(re); |     const ch = new Checker(re); | ||||||
| 
 | 
 | ||||||
|     const mirGen = new MirGen(re, ch); |     const mirGen = new MirGen(re, ch); | ||||||
| 
 | 
 | ||||||
|     console.log("=== MIR ==="); |     // console.log("=== MIR ===");
 | ||||||
|     for (const stmt of ast) { |     // for (const stmt of ast) {
 | ||||||
|         if (stmt.kind.tag !== "fn") { |     //     if (stmt.kind.tag !== "fn") {
 | ||||||
|             throw new Error("only functions can compile top level"); |     //         throw new Error("only functions can compile top level");
 | ||||||
|         } |     //     }
 | ||||||
|         const fnMir = mirGen.fnMir(stmt, stmt.kind); |     //     const fnMir = mirGen.fnMir(stmt, stmt.kind);
 | ||||||
|         console.log(new FnStringifyer(fnMir).stringify()); |     //     console.log(new FnStringifyer(fnMir).stringify());
 | ||||||
|     } |     // }
 | ||||||
| 
 | 
 | ||||||
|     const lir = new LirGen(ast, mirGen).generate(); |     const lir = new LirGen(ast, mirGen).generate(); | ||||||
|     console.log("=== LIR ==="); |     // console.log("=== LIR ===");
 | ||||||
|     console.log(new ProgramStringifyer(lir).stringify()); |     // console.log(new ProgramStringifyer(lir).stringify());
 | ||||||
|  | 
 | ||||||
|  |     lirOptimize(lir); | ||||||
| 
 | 
 | ||||||
|     const asm = new AsmGen(lir).generate(); |     const asm = new AsmGen(lir).generate(); | ||||||
|     console.log("=== ASM ==="); |     // console.log("=== ASM ===");
 | ||||||
|     console.log(asm); |     // console.log(asm);
 | ||||||
|  | 
 | ||||||
|  |     await Deno.writeTextFile("out.nasm", asm); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| main(); | main(); | ||||||
|  | |||||||
| @ -51,11 +51,12 @@ export class FnMirGen { | |||||||
| 
 | 
 | ||||||
|         const entry = this.block(); |         const entry = this.block(); | ||||||
|         const exit = this.block(); |         const exit = this.block(); | ||||||
|  |         this.returnBlock = exit; | ||||||
| 
 | 
 | ||||||
|         this.currentBlock = entry; |         this.currentBlock = entry; | ||||||
|         this.lowerBlock(this.stmtKind.body); |         this.lowerBlock(this.stmtKind.body); | ||||||
| 
 | 
 | ||||||
|         entry.ter = Ter({ tag: "goto", target: exit }); |         this.currentBlock.ter = Ter({ tag: "goto", target: exit }); | ||||||
|         exit.ter = Ter({ tag: "return" }); |         exit.ter = Ter({ tag: "return" }); | ||||||
|         return { |         return { | ||||||
|             stmt: this.stmt, |             stmt: this.stmt, | ||||||
| @ -251,6 +252,8 @@ export class FnMirGen { | |||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|             case "binary": { |             case "binary": { | ||||||
|  |                 this.lowerExpr(k.left); | ||||||
|  |                 this.lowerExpr(k.right); | ||||||
|                 switch (k.op) { |                 switch (k.op) { | ||||||
|                     case "<": |                     case "<": | ||||||
|                         this.pushStmt({ tag: "lt" }); |                         this.pushStmt({ tag: "lt" }); | ||||||
| @ -290,4 +293,3 @@ export class FnMirGen { | |||||||
|         this.currentBlock.stmts.push(Stmt(kind)); |         this.currentBlock.stmts.push(Stmt(kind)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
|  | |||||||
| @ -2,13 +2,15 @@ | |||||||
| #[c_function("print_int")] | #[c_function("print_int")] | ||||||
| fn print_int(value) {} | fn print_int(value) {} | ||||||
| 
 | 
 | ||||||
| // #[c_function("println")] | fn inner(value) { | ||||||
| // fn println(value) {} |     print_int(value); | ||||||
|  |     return 0;     | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| fn main() { | fn main() { | ||||||
|     // println("hello\ world"); |  | ||||||
|     let a = 4; |     let a = 4; | ||||||
|     print_int(a + 2); |     inner(a + 2); | ||||||
|  |     return a; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // vim: syntax=rust commentstring=//\ %s | // vim: syntax=rust commentstring=//\ %s | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user