Skip to content

Commit 344be3c

Browse files
authored
Compile multiple files as one (.vm named after the first one) so we can inline itoa into a new fact.asm that shows both recursive and iterative factorial code (#42)
* Compile multiple files as one (.vm named after the first one) so we can inline itoa into a new fact.asm that shows both recursive and iterative factorial code * Add a print util to not repeat the loadi * minor lint and also exercize the cvm for fact. shows bug with incrs * fix cvm to match loading accumulator content upon incrs * abort on any file error * less silly end of Compile * Add a comment explaining the weird stack var if
1 parent 8bb8962 commit 344be3c

File tree

5 files changed

+97
-26
lines changed

5 files changed

+97
-26
lines changed

Makefile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ grol_cvm: Makefile cvm/cvm.c cvm/cvm.h
5252
cvm-loop: grol_cvm
5353
time ./grol_cvm programs/loop.vm
5454

55+
fact: vm grol_cvm
56+
./vm compile programs/fact.asm programs/itoa.asm
57+
./vm run -quiet programs/fact.vm
58+
./grol_cvm programs/fact.vm
59+
5560
debug-cvm: Makefile cvm/cvm.c cvm/cvm.h
5661
$(CC) -O3 -Wall -Wextra -pedantic -Werror -DDEBUG=1 -o grol_cvm cvm/cvm.c
5762
./grol_cvm programs/simple.vm
@@ -80,7 +85,7 @@ install:
8085
vm version
8186

8287

83-
test: vm unit-tests itoa-test
88+
test: vm unit-tests itoa-test fact
8489

8590
unit-tests:
8691
CGO_ENABLED=0 go test -tags $(GO_BUILD_TAGS) ./...
@@ -100,7 +105,7 @@ cpu/syscall_string.go: cpu/syscall.go
100105
go generate ./cpu # if this fails go install golang.org/x/tools/cmd/stringer@latest
101106

102107
.PHONY: all lint generate test clean run build install unit-tests
103-
.PHONY: show_cpu_profile show_mem_profile native debug-cvm
108+
.PHONY: show_cpu_profile show_mem_profile native debug-cvm fact
104109

105110
show_cpu_profile:
106111
-pkill pprof

asm/asm.go

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,33 +24,35 @@ type Line struct {
2424
}
2525

2626
func Compile(files ...string) int {
27-
for _, file := range files {
27+
readers := make([]io.Reader, 0, len(files))
28+
var writer *bufio.Writer
29+
for i, file := range files {
2830
log.Infof("Compiling file: %s", file)
2931
f, err := os.Open(file)
3032
if err != nil {
31-
log.Errf("Failed to open file %s: %v", file, err)
32-
continue
33+
return log.FErrf("Failed to open file %s: %v", file, err)
3334
}
3435
defer f.Close()
3536
// replace .asm with .vm
36-
outputFile := strings.TrimSuffix(file, ".asm") + ".vm"
37-
log.Infof("Output file: %s", outputFile)
38-
out, err := os.Create(outputFile)
39-
if err != nil {
40-
log.Errf("Failed to create output file %s: %v", outputFile, err)
41-
continue
37+
if !strings.HasSuffix(file, ".asm") {
38+
return log.FErrf("Invalid file extension for %s, expected .asm", file)
4239
}
43-
defer out.Close()
44-
writer := bufio.NewWriter(out)
45-
defer writer.Flush()
46-
_, _ = writer.WriteString(cpu.HEADER)
47-
reader := bufio.NewReader(f)
48-
ret := compile(reader, writer)
49-
if ret != 0 {
50-
return ret
40+
if i == 0 {
41+
outputFile := strings.TrimSuffix(file, ".asm") + ".vm"
42+
log.Infof("Output file: %s", outputFile)
43+
out, err := os.Create(outputFile)
44+
if err != nil {
45+
return log.FErrf("Failed to create output file %s: %v", outputFile, err)
46+
}
47+
defer out.Close()
48+
writer = bufio.NewWriter(out)
49+
defer writer.Flush()
50+
_, _ = writer.WriteString(cpu.HEADER)
5151
}
52+
readers = append(readers, f)
5253
}
53-
return 0
54+
reader := bufio.NewReader(io.MultiReader(readers...))
55+
return compile(reader, writer)
5456
}
5557

5658
//nolint:gocyclo // it's a full parser.
@@ -284,8 +286,8 @@ func compile(reader *bufio.Reader, writer *bufio.Writer) int {
284286
op = op.SetOpcode(cpu.Ret)
285287
op = op.SetOperand(cpu.ImmediateData(returnN))
286288
log.Debugf("Return -> Ret %d", returnN)
287-
returnN = 0
288-
clear(varmap)
289+
// Don't reset returnN or varmap because there could be more than 1 return
290+
// point.
289291
default:
290292
instrEnum, ok := cpu.InstructionFromString(instr)
291293
if !ok {
@@ -294,9 +296,15 @@ func compile(reader *bufio.Reader, writer *bufio.Writer) int {
294296
log.Debugf("Parsing instruction: %s %v", instrEnum, args)
295297
if instrEnum >= cpu.LoadS { // for stack instructions, resolve var references
296298
for i, v := range args {
299+
if !isAddressLabel(v) {
300+
continue
301+
}
297302
if idx, ok := varmap[v]; ok {
298303
log.Debugf("Resolved var %s to index %d", v, idx)
299304
args[i] = fmt.Sprintf("%d", idx)
305+
} else if instrEnum != cpu.SysS || i != 0 {
306+
// First argument of SysS is the syscall name not a stack variable.
307+
return log.FErrf("Unknown stack variable: %s", v)
300308
}
301309
}
302310
}

cpu/cpu.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ func execute(pc ImmediateData, program []Operation, accumulator int64) (int64, i
348348
pc = ImmediateData(stack[stackPtr])
349349
stackPtr--
350350
if Debug {
351-
log.Debugf("Return at PC: %d, returning to PC: %d - SP = %d %v", oldPC, pc, stackPtr, stack[:stackPtr+1])
351+
log.Debugf("Return at PC: %d, returning to PC: %d - SP = %d %v", oldPC, pc, stackPtr, stack[:stackPtr+1])
352352
}
353353
continue
354354
case Push:
@@ -415,10 +415,12 @@ func execute(pc ImmediateData, program []Operation, accumulator int64) (int64, i
415415
arg := op.Operand()
416416
offset := int(arg >> 8)
417417
value := int8(arg & 0xff) //nolint:gosec // 0xff implies can't overflow (and we want the sign bit too)
418-
stack[stackPtr-offset] += Operation(value)
418+
oldValue := stack[stackPtr-offset]
419+
accumulator = int64(oldValue) + int64(value)
420+
stack[stackPtr-offset] = Operation(accumulator)
419421
if Debug {
420422
log.Debugf("IncrS at PC: %d, offset: %d, value: %d -> %d - SP = %d %v",
421-
pc, offset, value, stack[stackPtr-offset], stackPtr, stack[:stackPtr+1])
423+
pc, offset, value, accumulator, stackPtr, stack[:stackPtr+1])
422424
}
423425
case IdivS:
424426
offset := int(op.Operand())

cvm/cvm.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,8 @@ void run_program(CPU *cpu) {
331331
int8_t value = (int8_t)(arg & 0xFF);
332332
DEBUG_PRINT("IncrS at PC %" PRId64 ", offset %d, by %d, SP=%d\n",
333333
cpu->pc, offset, value, stack_ptr);
334-
stack[stack_ptr - offset] += (Operation)value;
334+
cpu->accumulator = (int64_t)stack[stack_ptr - offset] + (int64_t)value;
335+
stack[stack_ptr - offset] = (Operation)cpu->accumulator;
335336
DEBUG_PRINT("IncrS new value %" PRId64 "\n",
336337
(int64_t)stack[stack_ptr - offset]);
337338
} break;

programs/fact.asm

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
; factorial
2+
; depends on itoa, so compile with
3+
; vm compile programs/fact.asm programs/itoa.asm
4+
5+
sys write fact_rec_str
6+
loadI 5
7+
call print
8+
call factrec
9+
call itoa
10+
sys write fact_iter_str
11+
loadI 7
12+
call print
13+
call facti
14+
call itoa
15+
sys exit 0
16+
17+
factrec: ; recursive factorial
18+
var n
19+
subi 1
20+
jnz more
21+
loadI 1
22+
return
23+
more:
24+
call factrec
25+
muls n
26+
return
27+
28+
facti: ; iterative factorial
29+
var result n
30+
subi 1
31+
storeS n
32+
loop:
33+
muls result
34+
storeS result
35+
incrs -1 n
36+
jnz loop
37+
end:
38+
loadS result
39+
return
40+
41+
; print accumulator and put its value back (instead of the bytes written returned by itoa)
42+
print:
43+
var acc
44+
sys write fact_str
45+
loadS acc
46+
call itoa
47+
loadS acc
48+
return
49+
50+
fact_rec_str:
51+
str8 "Recursive "
52+
fact_iter_str:
53+
str8 "Iterative "
54+
fact_str:
55+
str8 "Factorial of "

0 commit comments

Comments
 (0)