Add my homegrown cross compiler scripts
This commit is contained in:
parent
df1c0486c5
commit
3bd86e6e1c
59
Rakefile
59
Rakefile
@ -7,28 +7,12 @@ require 'open3'
|
|||||||
require 'rake/clean'
|
require 'rake/clean'
|
||||||
require 'term/ansicolor'
|
require 'term/ansicolor'
|
||||||
|
|
||||||
CLOBBER.include 'build'
|
CLEAN.include 'build/boot'
|
||||||
|
|
||||||
CROSS_GCC = '../riscv32-ilp32d--glibc/bin/riscv32-linux-gcc'
|
directory 'build/boot'
|
||||||
SYSROOT = '../riscv32-ilp32d--glibc/riscv32-buildroot-linux-gnu/sysroot'
|
|
||||||
QEMU = 'qemu-riscv32'
|
|
||||||
|
|
||||||
def assemble_stage(output, compiler, source)
|
|
||||||
arguments = [QEMU, '-L', SYSROOT, *compiler]
|
|
||||||
|
|
||||||
puts Term::ANSIColor.green(arguments * ' ')
|
|
||||||
puts
|
|
||||||
Open3.popen2(*arguments) do |qemu_in, qemu_out|
|
|
||||||
qemu_in.write File.read(*source)
|
|
||||||
qemu_in.close
|
|
||||||
|
|
||||||
IO.copy_stream qemu_out, output
|
|
||||||
qemu_out.close
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Final stage'
|
desc 'Final stage'
|
||||||
task default: ['build/stage2b', 'build/stage2b.s', 'boot/stage2.elna'] do |t|
|
task default: ['build/boot/stage2b', 'build/boot/stage2b.s', 'boot/stage2.elna'] do |t|
|
||||||
exe, previous_output, source = t.prerequisites
|
exe, previous_output, source = t.prerequisites
|
||||||
|
|
||||||
cat_arguments = ['cat', source]
|
cat_arguments = ['cat', source]
|
||||||
@ -36,40 +20,3 @@ task default: ['build/stage2b', 'build/stage2b.s', 'boot/stage2.elna'] do |t|
|
|||||||
diff_arguments = ['diff', '-Nur', '--text', previous_output, '-']
|
diff_arguments = ['diff', '-Nur', '--text', previous_output, '-']
|
||||||
Open3.pipeline(cat_arguments, compiler_arguments, diff_arguments)
|
Open3.pipeline(cat_arguments, compiler_arguments, diff_arguments)
|
||||||
end
|
end
|
||||||
|
|
||||||
directory 'build'
|
|
||||||
|
|
||||||
Dir.glob('boot/*.s').each do |assembly_source|
|
|
||||||
target_object = Pathname.new('build') + Pathname.new(assembly_source).basename.sub_ext('.o')
|
|
||||||
|
|
||||||
file target_object.to_s => [assembly_source, 'build'] do |t|
|
|
||||||
sh CROSS_GCC, '-c', '-o', t.name, assembly_source
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
desc 'Initial stage'
|
|
||||||
file 'build/stage1' => ['build/tokenizer.o', 'build/stage1.o', 'build/common-boot.o'] do |t|
|
|
||||||
sh CROSS_GCC, '-nostdlib', '-o', t.name, *t.prerequisites
|
|
||||||
end
|
|
||||||
|
|
||||||
file 'build/stage2a.s' => ['build/stage1', 'boot/stage2.elna'] do |t|
|
|
||||||
source, exe = t.prerequisites.partition { |prerequisite| prerequisite.end_with? '.elna' }
|
|
||||||
|
|
||||||
File.open t.name, 'w' do |output|
|
|
||||||
assemble_stage output, exe, source
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
['build/stage2a', 'build/stage2b'].each do |exe|
|
|
||||||
file exe => [exe.ext('.s'), 'build/common-boot.o'] do |t|
|
|
||||||
sh CROSS_GCC, '-nostdlib', '-o', t.name, *t.prerequisites
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
file 'build/stage2b.s' => ['build/stage2a', 'boot/stage2.elna'] do |t|
|
|
||||||
source, exe = t.prerequisites.partition { |prerequisite| prerequisite.end_with? '.elna' }
|
|
||||||
|
|
||||||
File.open t.name, 'w' do |output|
|
|
||||||
assemble_stage output, exe, source
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
@ -34,20 +34,21 @@
|
|||||||
.equ TOKEN_IDENTIFIER, 27
|
.equ TOKEN_IDENTIFIER, 27
|
||||||
# The constant should match the character index in the byte_keywords string.
|
# The constant should match the character index in the byte_keywords string.
|
||||||
|
|
||||||
.equ TOKEN_AND, 28
|
.equ TOKEN_AND, TOKEN_IDENTIFIER + 1
|
||||||
.equ TOKEN_DOT, 29
|
.equ TOKEN_DOT, TOKEN_IDENTIFIER + 2
|
||||||
.equ TOKEN_COMMA, 30
|
.equ TOKEN_COMMA, TOKEN_IDENTIFIER + 3
|
||||||
.equ TOKEN_COLON, 31
|
.equ TOKEN_COLON, TOKEN_IDENTIFIER + 4
|
||||||
.equ TOKEN_SEMICOLON, 32
|
.equ TOKEN_SEMICOLON, TOKEN_IDENTIFIER + 5
|
||||||
.equ TOKEN_LEFT_PAREN, 33
|
.equ TOKEN_LEFT_PAREN, TOKEN_IDENTIFIER + 6
|
||||||
.equ TOKEN_RIGHT_PAREN, 34
|
.equ TOKEN_RIGHT_PAREN, TOKEN_IDENTIFIER + 7
|
||||||
.equ TOKEN_LEFT_BRACKET, 35
|
.equ TOKEN_LEFT_BRACKET, TOKEN_IDENTIFIER + 8
|
||||||
.equ TOKEN_RIGHT_BRACKET, 36
|
.equ TOKEN_RIGHT_BRACKET, TOKEN_IDENTIFIER + 9
|
||||||
.equ TOKEN_HAT, 37
|
.equ TOKEN_HAT, TOKEN_IDENTIFIER + 10
|
||||||
.equ TOKEN_EQUALS, 38
|
.equ TOKEN_EQUALS, TOKEN_IDENTIFIER + 11
|
||||||
.equ TOKEN_PLUS, 39
|
.equ TOKEN_PLUS, TOKEN_IDENTIFIER + 12
|
||||||
.equ TOKEN_MINUS, 40
|
.equ TOKEN_MINUS, TOKEN_IDENTIFIER + 13
|
||||||
.equ TOKEN_ASTERISK, 41
|
.equ TOKEN_ASTERISK, TOKEN_IDENTIFIER + 14
|
||||||
.equ TOKEN_AT, 42
|
.equ TOKEN_AT, TOKEN_IDENTIFIER + 15
|
||||||
|
|
||||||
.equ TOKEN_ASSIGN, 43
|
.equ TOKEN_ASSIGN, 43
|
||||||
|
.equ TOKEN_INTEGER, 44
|
||||||
|
372
boot/stage1.s
372
boot/stage1.s
@ -4,9 +4,29 @@
|
|||||||
|
|
||||||
.global _start # Program entry point.
|
.global _start # Program entry point.
|
||||||
|
|
||||||
|
#
|
||||||
# Registers used as global variables:
|
# Registers used as global variables:
|
||||||
# s1 - Contains the current position in the source text.
|
# s1 - Contains the current position in the source text.
|
||||||
# s2 - Label counter.
|
# s2 - Label counter.
|
||||||
|
#
|
||||||
|
# - The compiler expects valid input, otherwise it will generate invalid
|
||||||
|
# assembly or hang. There is no error checking, no semantic analysis, no
|
||||||
|
# type checking.
|
||||||
|
#
|
||||||
|
# - Imports with only a module name without package, e.g.
|
||||||
|
# "import dummy", can be parsed, but are ignored.
|
||||||
|
#
|
||||||
|
# - No loops. Only labels and goto.
|
||||||
|
#
|
||||||
|
# - Only unsigned number literals are supported (in decimal or
|
||||||
|
# hexadecimal format).
|
||||||
|
#
|
||||||
|
# - Comments are accepted only at the end of a line.
|
||||||
|
#
|
||||||
|
# - Return can be used only as the last statement of a procedure. It
|
||||||
|
# doesn't actually return, but sets a0 to the appropriate value.
|
||||||
|
#
|
||||||
|
# - The lvalue of an assignment can only be an identifier.
|
||||||
|
|
||||||
.include "boot/definitions.inc"
|
.include "boot/definitions.inc"
|
||||||
|
|
||||||
@ -73,9 +93,6 @@ _compile_import:
|
|||||||
addi s0, sp, 24
|
addi s0, sp, 24
|
||||||
|
|
||||||
.Lcompile_import_loop:
|
.Lcompile_import_loop:
|
||||||
call _skip_comment
|
|
||||||
call _skip_spaces
|
|
||||||
|
|
||||||
mv a0, s1
|
mv a0, s1
|
||||||
addi a1, sp, 0
|
addi a1, sp, 0
|
||||||
call _tokenize_next
|
call _tokenize_next
|
||||||
@ -106,7 +123,6 @@ _build_binary_expression:
|
|||||||
|
|
||||||
li a0, 0
|
li a0, 0
|
||||||
call _build_expression
|
call _build_expression
|
||||||
call _skip_spaces
|
|
||||||
|
|
||||||
mv a0, s1
|
mv a0, s1
|
||||||
addi a1, sp, 12
|
addi a1, sp, 12
|
||||||
@ -114,26 +130,26 @@ _build_binary_expression:
|
|||||||
lw t0, 12(sp)
|
lw t0, 12(sp)
|
||||||
|
|
||||||
li t1, TOKEN_AND
|
li t1, TOKEN_AND
|
||||||
beq t0, t1, .L_build_binary_expression_and
|
beq t0, t1, .Lbuild_binary_expression_and
|
||||||
|
|
||||||
li t1, TOKEN_OR
|
li t1, TOKEN_OR
|
||||||
beq t0, t1, .L_build_binary_expression_or
|
beq t0, t1, .Lbuild_binary_expression_or
|
||||||
|
|
||||||
li t1, TOKEN_PLUS
|
li t1, TOKEN_PLUS
|
||||||
beq t0, t1, .L_build_binary_expression_plus
|
beq t0, t1, .Lbuild_binary_expression_plus
|
||||||
|
|
||||||
li t1, TOKEN_EQUALS
|
li t1, TOKEN_EQUALS
|
||||||
beq t0, t1, .L_build_binary_expression_equal
|
beq t0, t1, .Lbuild_binary_expression_equal
|
||||||
|
|
||||||
li t1, TOKEN_ASTERISK
|
li t1, TOKEN_ASTERISK
|
||||||
beq t0, t1, .L_build_binary_expression_product
|
beq t0, t1, .Lbuild_binary_expression_product
|
||||||
|
|
||||||
li t1, TOKEN_MINUS
|
li t1, TOKEN_MINUS
|
||||||
beq t0, t1, .L_build_binary_expression_minus
|
beq t0, t1, .Lbuild_binary_expression_minus
|
||||||
|
|
||||||
j .Lbuild_binary_expression_end
|
j .Lbuild_binary_expression_end
|
||||||
|
|
||||||
.L_build_binary_expression_equal:
|
.Lbuild_binary_expression_equal:
|
||||||
mv s1, a0 # Skip =.
|
mv s1, a0 # Skip =.
|
||||||
li a0, 1
|
li a0, 1
|
||||||
call _build_expression
|
call _build_expression
|
||||||
@ -147,7 +163,7 @@ _build_binary_expression:
|
|||||||
|
|
||||||
j .Lbuild_binary_expression_end
|
j .Lbuild_binary_expression_end
|
||||||
|
|
||||||
.L_build_binary_expression_and:
|
.Lbuild_binary_expression_and:
|
||||||
mv s1, a0 # Skip &.
|
mv s1, a0 # Skip &.
|
||||||
li a0, 1
|
li a0, 1
|
||||||
call _build_expression
|
call _build_expression
|
||||||
@ -157,7 +173,7 @@ _build_binary_expression:
|
|||||||
|
|
||||||
j .Lbuild_binary_expression_end
|
j .Lbuild_binary_expression_end
|
||||||
|
|
||||||
.L_build_binary_expression_or:
|
.Lbuild_binary_expression_or:
|
||||||
mv s1, a0 # Skip or.
|
mv s1, a0 # Skip or.
|
||||||
li a0, 1
|
li a0, 1
|
||||||
call _build_expression
|
call _build_expression
|
||||||
@ -167,7 +183,7 @@ _build_binary_expression:
|
|||||||
|
|
||||||
j .Lbuild_binary_expression_end
|
j .Lbuild_binary_expression_end
|
||||||
|
|
||||||
.L_build_binary_expression_plus:
|
.Lbuild_binary_expression_plus:
|
||||||
mv s1, a0 # Skip +.
|
mv s1, a0 # Skip +.
|
||||||
li a0, 1
|
li a0, 1
|
||||||
call _build_expression
|
call _build_expression
|
||||||
@ -177,7 +193,7 @@ _build_binary_expression:
|
|||||||
|
|
||||||
j .Lbuild_binary_expression_end
|
j .Lbuild_binary_expression_end
|
||||||
|
|
||||||
.L_build_binary_expression_minus:
|
.Lbuild_binary_expression_minus:
|
||||||
mv s1, a0 # Skip -.
|
mv s1, a0 # Skip -.
|
||||||
li a0, 1
|
li a0, 1
|
||||||
call _build_expression
|
call _build_expression
|
||||||
@ -187,7 +203,7 @@ _build_binary_expression:
|
|||||||
|
|
||||||
j .Lbuild_binary_expression_end
|
j .Lbuild_binary_expression_end
|
||||||
|
|
||||||
.L_build_binary_expression_product:
|
.Lbuild_binary_expression_product:
|
||||||
mv s1, a0 # Skip *.
|
mv s1, a0 # Skip *.
|
||||||
li a0, 1
|
li a0, 1
|
||||||
call _build_expression
|
call _build_expression
|
||||||
@ -448,7 +464,6 @@ _build_expression:
|
|||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
addi s1, s1, 1 # Skip @.
|
addi s1, s1, 1 # Skip @.
|
||||||
call _skip_spaces
|
|
||||||
call _read_token
|
call _read_token
|
||||||
sw s1, 32(sp)
|
sw s1, 32(sp)
|
||||||
sw a0, 28(sp)
|
sw a0, 28(sp)
|
||||||
@ -654,7 +669,6 @@ _compile_call:
|
|||||||
sw zero, 12(sp) # Argument count for a procedure call.
|
sw zero, 12(sp) # Argument count for a procedure call.
|
||||||
|
|
||||||
.Lcompile_call_paren:
|
.Lcompile_call_paren:
|
||||||
call _skip_spaces
|
|
||||||
lbu t0, (s1)
|
lbu t0, (s1)
|
||||||
li t1, 0x29 # )
|
li t1, 0x29 # )
|
||||||
beq t0, t1, .Lcompile_call_complete
|
beq t0, t1, .Lcompile_call_complete
|
||||||
@ -688,7 +702,6 @@ _compile_call:
|
|||||||
li a1, 5
|
li a1, 5
|
||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
call _skip_spaces
|
|
||||||
lbu t0, (s1)
|
lbu t0, (s1)
|
||||||
li t1, ','
|
li t1, ','
|
||||||
bne t0, t1, .Lcompile_call_paren
|
bne t0, t1, .Lcompile_call_paren
|
||||||
@ -726,7 +739,6 @@ _compile_call:
|
|||||||
li a0, '\n'
|
li a0, '\n'
|
||||||
call _put_char
|
call _put_char
|
||||||
|
|
||||||
call _skip_spaces
|
|
||||||
addi s1, s1, 1 # Skip the close paren.
|
addi s1, s1, 1 # Skip the close paren.
|
||||||
|
|
||||||
# Epilogue.
|
# Epilogue.
|
||||||
@ -912,7 +924,6 @@ _compile_procedure_section:
|
|||||||
.Lcompile_procedure_section_loop:
|
.Lcompile_procedure_section_loop:
|
||||||
call _skip_spaces
|
call _skip_spaces
|
||||||
call _skip_comment
|
call _skip_comment
|
||||||
call _skip_spaces
|
|
||||||
|
|
||||||
mv a0, s1
|
mv a0, s1
|
||||||
addi a1, sp, 4
|
addi a1, sp, 4
|
||||||
@ -945,7 +956,6 @@ _compile_module_declaration:
|
|||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
# Skip "program".
|
# Skip "program".
|
||||||
call _skip_comment
|
|
||||||
mv a0, s1
|
mv a0, s1
|
||||||
addi a1, sp, 4
|
addi a1, sp, 4
|
||||||
call _tokenize_next
|
call _tokenize_next
|
||||||
@ -965,9 +975,6 @@ _compile_constant_section:
|
|||||||
sw s0, 16(sp)
|
sw s0, 16(sp)
|
||||||
addi s0, sp, 24
|
addi s0, sp, 24
|
||||||
|
|
||||||
call _skip_comment
|
|
||||||
call _skip_spaces
|
|
||||||
|
|
||||||
mv a0, s1
|
mv a0, s1
|
||||||
addi a1, sp, 4
|
addi a1, sp, 4
|
||||||
call _tokenize_next
|
call _tokenize_next
|
||||||
@ -999,25 +1006,22 @@ _compile_constant_section:
|
|||||||
.type _compile_constant, @function
|
.type _compile_constant, @function
|
||||||
_compile_constant:
|
_compile_constant:
|
||||||
# Prologue.
|
# Prologue.
|
||||||
addi sp, sp, -24
|
addi sp, sp, -32
|
||||||
sw ra, 20(sp)
|
sw ra, 28(sp)
|
||||||
sw s0, 16(sp)
|
sw s0, 24(sp)
|
||||||
addi s0, sp, 24
|
addi s0, sp, 32
|
||||||
|
|
||||||
mv a0, s1
|
mv a0, s1
|
||||||
addi a1, sp, 4
|
addi a1, sp, 12
|
||||||
call _tokenize_next
|
call _tokenize_next
|
||||||
|
addi a1, sp, 0
|
||||||
sub a1, a0, s1 # The identifier end from _tokenize_next should be in a0.
|
call _tokenize_next # Skip the assignment sign.
|
||||||
mv a0, s1
|
mv s1, a0
|
||||||
add s1, s1, a1 # Save the identifier pointer before advancing it.
|
# Write identifier the identifier.
|
||||||
|
lw a0, 20(sp)
|
||||||
|
lw a1, 16(sp)
|
||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
mv a0, s1
|
|
||||||
addi a1, sp, 4
|
|
||||||
call _tokenize_next
|
|
||||||
mv s1, a0 # Skip the assignment sign.
|
|
||||||
|
|
||||||
# : .long
|
# : .long
|
||||||
li t0, 0x20676e6f # ong_
|
li t0, 0x20676e6f # ong_
|
||||||
sw t0, 4(sp)
|
sw t0, 4(sp)
|
||||||
@ -1027,21 +1031,23 @@ _compile_constant:
|
|||||||
li a1, 8
|
li a1, 8
|
||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
call _skip_spaces
|
mv a0, s1
|
||||||
call _read_token
|
addi a1, sp, 12
|
||||||
|
call _tokenize_next
|
||||||
|
mv s1, a0
|
||||||
|
|
||||||
mv a1, a0 # The literal length from _read_token should be in a1.
|
lw a0, 20(sp) # Save the literal pointer before advancing it.
|
||||||
mv a0, s1 # Save the literal pointer before advancing it.
|
lw a1, 16(sp) # The literal length.
|
||||||
add s1, s1, a1
|
|
||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
li a0, '\n'
|
li a0, '\n'
|
||||||
call _put_char
|
call _put_char
|
||||||
|
call _skip_spaces
|
||||||
|
|
||||||
# Epilogue.
|
# Epilogue.
|
||||||
lw ra, 20(sp)
|
lw ra, 28(sp)
|
||||||
lw s0, 16(sp)
|
lw s0, 24(sp)
|
||||||
addi sp, sp, 24
|
addi sp, sp, 32
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.type _compile_variable_section, @function
|
.type _compile_variable_section, @function
|
||||||
@ -1080,6 +1086,7 @@ _compile_variable_section:
|
|||||||
addi sp, sp, 24
|
addi sp, sp, 24
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
# Compile a global variable.
|
||||||
.type _compile_variable, @function
|
.type _compile_variable, @function
|
||||||
_compile_variable:
|
_compile_variable:
|
||||||
# Prologue.
|
# Prologue.
|
||||||
@ -1088,32 +1095,33 @@ _compile_variable:
|
|||||||
sw s0, 40(sp)
|
sw s0, 40(sp)
|
||||||
addi s0, sp, 48
|
addi s0, sp, 48
|
||||||
|
|
||||||
call _read_token
|
|
||||||
|
|
||||||
# Save the identifier on the stack since it should emitted multiple times.
|
# Save the identifier on the stack since it should emitted multiple times.
|
||||||
sw s1, 36(sp)
|
mv a0, s1
|
||||||
sw a0, 32(sp)
|
addi a1, sp, 28
|
||||||
add s1, s1, a0
|
call _tokenize_next
|
||||||
|
addi a1, sp, 4
|
||||||
|
call _tokenize_next # Skip the colon in front of the type.
|
||||||
|
addi a1, sp, 4
|
||||||
|
call _tokenize_next # Skip the opening bracket.
|
||||||
|
addi a1, sp, 16
|
||||||
|
call _tokenize_next # Save the array size on the stack since it has to be emitted multiple times.
|
||||||
|
addi a1, sp, 4
|
||||||
|
call _tokenize_next # Skip the closing bracket.
|
||||||
|
addi a1, sp, 4
|
||||||
|
call _tokenize_next # Skip the type.
|
||||||
|
mv s1, a0
|
||||||
|
|
||||||
call _skip_spaces
|
/* DEBUG
|
||||||
addi s1, s1, 1 # Skip the colon in front of the type.
|
lw a0, 24(sp)
|
||||||
|
add a0, a0, '0'
|
||||||
call _skip_spaces
|
|
||||||
addi s1, s1, 1 # Skip the opening bracket.
|
|
||||||
|
|
||||||
call _read_token
|
|
||||||
|
|
||||||
# Save the array size on the stack since it has to be emitted multiple times.
|
|
||||||
sw s1, 28(sp)
|
|
||||||
sw a0, 24(sp)
|
sw a0, 24(sp)
|
||||||
add s1, s1, a0
|
addi a0, sp, 24
|
||||||
|
li a1, 1
|
||||||
call _skip_spaces
|
call _write_error
|
||||||
addi s1, s1, 1 # Skip the closing bracket.
|
lw a0, 28(sp)
|
||||||
|
li a1, 8
|
||||||
call _skip_spaces
|
call _write_error
|
||||||
call _read_token
|
*/
|
||||||
add s1, s1, a0 # Skip the type.
|
|
||||||
|
|
||||||
# .type identifier, @object
|
# .type identifier, @object
|
||||||
la a0, asm_type
|
la a0, asm_type
|
||||||
@ -1134,15 +1142,15 @@ _compile_variable:
|
|||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
li t0, 0x206f7265 # ero_
|
li t0, 0x206f7265 # ero_
|
||||||
sw t0, 20(sp)
|
sw t0, 12(sp)
|
||||||
li t0, 0x7a2e203a # : .z
|
li t0, 0x7a2e203a # : .z
|
||||||
sw t0, 16(sp)
|
sw t0, 8(sp)
|
||||||
addi a0, sp, 16
|
addi a0, sp, 8
|
||||||
li a1, 8
|
li a1, 8
|
||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
lw a0, 28(sp)
|
lw a0, 24(sp)
|
||||||
lw a1, 24(sp)
|
lw a1, 20(sp)
|
||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
li a0, '\n'
|
li a0, '\n'
|
||||||
@ -1215,9 +1223,6 @@ _compile_procedure:
|
|||||||
|
|
||||||
# Generate the body of the procedure.
|
# Generate the body of the procedure.
|
||||||
.Lcompile_procedure_body:
|
.Lcompile_procedure_body:
|
||||||
call _skip_spaces
|
|
||||||
call _read_line
|
|
||||||
sw a0, 12(sp)
|
|
||||||
li t0, 0x0a646e65 # end\n
|
li t0, 0x0a646e65 # end\n
|
||||||
sw t0, 8(sp)
|
sw t0, 8(sp)
|
||||||
mv a0, s1
|
mv a0, s1
|
||||||
@ -1227,7 +1232,6 @@ _compile_procedure:
|
|||||||
|
|
||||||
beqz a0, .Lcompile_procedure_end
|
beqz a0, .Lcompile_procedure_end
|
||||||
|
|
||||||
lw a0, 12(sp)
|
|
||||||
call _compile_statement
|
call _compile_statement
|
||||||
j .Lcompile_procedure_body
|
j .Lcompile_procedure_body
|
||||||
|
|
||||||
@ -1245,111 +1249,95 @@ _compile_procedure:
|
|||||||
addi sp, sp, 32
|
addi sp, sp, 32
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
# Compiles a goto statement to an uncoditional jump.
|
||||||
.type _compile_goto, @function
|
.type _compile_goto, @function
|
||||||
_compile_goto:
|
_compile_goto:
|
||||||
# Prologue.
|
# Prologue.
|
||||||
addi sp, sp, -16
|
addi sp, sp, -32
|
||||||
sw ra, 12(sp)
|
sw ra, 28(sp)
|
||||||
sw s0, 8(sp)
|
sw s0, 24(sp)
|
||||||
addi s0, sp, 16
|
addi s0, sp, 32
|
||||||
|
|
||||||
addi s1, s1, 4 # Skip the goto keyword.
|
mv a0, s1
|
||||||
|
addi a1, sp, 0
|
||||||
|
call _tokenize_next # Skip the goto keyword.
|
||||||
|
addi a1, sp, 0
|
||||||
|
call _tokenize_next # We should be on dot the label is beginning with.
|
||||||
|
addi a1, sp, 0
|
||||||
|
call _tokenize_next# Save the label name.
|
||||||
|
mv s1, a0
|
||||||
|
|
||||||
li t0, 0x206a # j_
|
li t0, 0x2e206a # j .
|
||||||
sw t0, 8(sp)
|
sw t0, 12(sp)
|
||||||
addi a0, sp, 8
|
addi a0, sp, 12
|
||||||
li a1, 2
|
li a1, 3
|
||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
call _skip_spaces
|
|
||||||
sw s1, 8(sp) # We should be on dot the label is beginning with.
|
|
||||||
addi s1, s1, 1
|
|
||||||
|
|
||||||
call _read_token
|
|
||||||
add s1, s1, a0
|
|
||||||
addi a1, a0, 1 # Label length and the dot.
|
|
||||||
lw a0, 8(sp) # Saved dot position.
|
lw a0, 8(sp) # Saved dot position.
|
||||||
|
lw a1, 4(sp)
|
||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
addi s1, s1, 1 # Skip the new line.
|
|
||||||
|
|
||||||
li a0, '\n'
|
li a0, '\n'
|
||||||
call _put_char
|
call _put_char
|
||||||
|
|
||||||
# Epilogue.
|
# Epilogue.
|
||||||
lw ra, 12(sp)
|
lw ra, 28(sp)
|
||||||
lw s0, 8(sp)
|
lw s0, 24(sp)
|
||||||
addi sp, sp, 16
|
addi sp, sp, 32
|
||||||
ret
|
ret
|
||||||
|
|
||||||
# a0 - Line length.
|
# Rewrites a label to assembly.
|
||||||
.type _compile_label, @function
|
.type _compile_label, @function
|
||||||
_compile_label:
|
_compile_label:
|
||||||
# Prologue.
|
# Prologue.
|
||||||
addi sp, sp, -16
|
addi sp, sp, -32
|
||||||
sw ra, 12(sp)
|
sw ra, 28(sp)
|
||||||
sw s0, 8(sp)
|
sw s0, 24(sp)
|
||||||
addi s0, sp, 16
|
addi s0, sp, 32
|
||||||
|
|
||||||
sw a0, 0(sp) # Save the line length.
|
|
||||||
mv a1, a0 # Argument for _write_out later.
|
|
||||||
|
|
||||||
lw t0, 0(sp) # Line length.
|
|
||||||
mv t1, s1 # Line start.
|
|
||||||
|
|
||||||
add t1, t1, t0
|
|
||||||
addi t1, t1, -1 # Last character on the line.
|
|
||||||
|
|
||||||
lbu t1, (t1)
|
|
||||||
li t2, ';'
|
|
||||||
bne t1, t2, .Lcompile_label_colon
|
|
||||||
|
|
||||||
addi a1, a1, -1
|
|
||||||
|
|
||||||
.Lcompile_label_colon:
|
|
||||||
# Write the whole line as is.
|
|
||||||
mv a0, s1
|
mv a0, s1
|
||||||
call _write_out
|
addi a1, sp, 8
|
||||||
|
call _tokenize_next # Dot starting the label.
|
||||||
|
addi a1, sp, 8
|
||||||
|
call _tokenize_next
|
||||||
|
mv s1, a0
|
||||||
|
|
||||||
li t0, 0x3a # :
|
li a0, '.'
|
||||||
sw t0, 4(sp)
|
call _put_char
|
||||||
addi a0, sp, 4
|
lw a0, 16(sp)
|
||||||
li a1, 1
|
lw a1, 12(sp)
|
||||||
call _write_out
|
call _write_out
|
||||||
|
li a0, ':'
|
||||||
li t0, '\n'
|
call _put_char
|
||||||
sw t0, 4(sp)
|
li a0, '\n'
|
||||||
addi a0, sp, 4
|
call _put_char
|
||||||
li a1, 1
|
|
||||||
call _write_out
|
|
||||||
|
|
||||||
lw a0, 0(sp)
|
|
||||||
addi a0, a0, 1 # Skip the new line as well.
|
|
||||||
add s1, s1, a0 # Skip the line.
|
|
||||||
|
|
||||||
# Epilogue.
|
# Epilogue.
|
||||||
lw ra, 12(sp)
|
lw ra, 28(sp)
|
||||||
lw s0, 8(sp)
|
lw s0, 24(sp)
|
||||||
addi sp, sp, 16
|
addi sp, sp, 32
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
# Just skips the return keyword and evaluates the return expression.
|
||||||
.type _compile_return, @function
|
.type _compile_return, @function
|
||||||
_compile_return:
|
_compile_return:
|
||||||
# Prologue.
|
# Prologue.
|
||||||
addi sp, sp, -16
|
addi sp, sp, -32
|
||||||
sw ra, 12(sp)
|
sw ra, 28(sp)
|
||||||
sw s0, 8(sp)
|
sw s0, 24(sp)
|
||||||
addi s0, sp, 16
|
addi s0, sp, 32
|
||||||
|
|
||||||
addi s1, s1, 6 # Skip return.
|
mv a0, s1
|
||||||
call _skip_spaces
|
addi a1, sp, 12
|
||||||
|
call _tokenize_next
|
||||||
|
mv s1, a0 # Skip return.
|
||||||
|
|
||||||
call _build_binary_expression
|
call _build_binary_expression
|
||||||
|
|
||||||
# Epilogue.
|
# Epilogue.
|
||||||
lw ra, 12(sp)
|
lw ra, 28(sp)
|
||||||
lw s0, 8(sp)
|
lw s0, 24(sp)
|
||||||
addi sp, sp, 16
|
addi sp, sp, 32
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.type _compile_if, @function
|
.type _compile_if, @function
|
||||||
@ -1403,8 +1391,6 @@ _compile_if:
|
|||||||
li t1, TOKEN_END
|
li t1, TOKEN_END
|
||||||
beq t0, t1, .Lcompile_if_end
|
beq t0, t1, .Lcompile_if_end
|
||||||
|
|
||||||
call _read_line
|
|
||||||
li a1, 1
|
|
||||||
call _compile_statement
|
call _compile_statement
|
||||||
|
|
||||||
j .Lcompile_if_loop
|
j .Lcompile_if_loop
|
||||||
@ -1422,7 +1408,7 @@ _compile_if:
|
|||||||
call _printi
|
call _printi
|
||||||
|
|
||||||
# Finalize the label.
|
# Finalize the label.
|
||||||
li t0, 0x0a3a # :\n:\n
|
li t0, 0x0a3a # :\n
|
||||||
sh t0, 16(sp)
|
sh t0, 16(sp)
|
||||||
addi a0, sp, 16
|
addi a0, sp, 16
|
||||||
li a1, 2
|
li a1, 2
|
||||||
@ -1436,11 +1422,7 @@ _compile_if:
|
|||||||
addi sp, sp, 32
|
addi sp, sp, 32
|
||||||
ret
|
ret
|
||||||
|
|
||||||
# Parameters:
|
# Checks for the type of the current statement and compiles it.
|
||||||
# a0 - Line length.
|
|
||||||
#
|
|
||||||
# Returns 1 in a0 if the parsed line contained a text section element such a
|
|
||||||
# procedure or the program entry point. Otherwise sets a0 to 0.
|
|
||||||
.type _compile_statement, @function
|
.type _compile_statement, @function
|
||||||
_compile_statement:
|
_compile_statement:
|
||||||
# Prologue.
|
# Prologue.
|
||||||
@ -1449,42 +1431,17 @@ _compile_statement:
|
|||||||
sw s0, 24(sp)
|
sw s0, 24(sp)
|
||||||
addi s0, sp, 32
|
addi s0, sp, 32
|
||||||
|
|
||||||
# Preserve passed arguments.
|
|
||||||
sw a0, 20(sp)
|
|
||||||
|
|
||||||
mv a0, s1
|
|
||||||
lw a1, 20(sp)
|
|
||||||
call _is_local_identifier
|
|
||||||
bnez a0, .Lcompile_statement_identifier
|
|
||||||
|
|
||||||
mv a0, s1
|
|
||||||
li a1, 2
|
|
||||||
call _is_register_identifier
|
|
||||||
bnez a0, .Lcompile_statement_identifier
|
|
||||||
|
|
||||||
li t0, 0x6f746f67 # goto
|
|
||||||
sw t0, 12(sp)
|
|
||||||
mv a0, s1
|
|
||||||
addi a1, sp, 12
|
|
||||||
li a2, 4
|
|
||||||
call _memcmp
|
|
||||||
beqz a0, .Lcompile_statement_goto
|
|
||||||
|
|
||||||
call _skip_comment
|
|
||||||
/* DEBUG
|
|
||||||
mv a0, s1
|
|
||||||
li a1, 4
|
|
||||||
call _write_error
|
|
||||||
mv a0, s1
|
|
||||||
li a1, 4
|
|
||||||
call _write_error
|
|
||||||
*/
|
|
||||||
|
|
||||||
mv a0, s1
|
mv a0, s1
|
||||||
addi a1, sp, 0
|
addi a1, sp, 0
|
||||||
call _tokenize_next
|
call _tokenize_next
|
||||||
lw t0, 0(sp)
|
lw t0, 0(sp)
|
||||||
|
|
||||||
|
li t1, TOKEN_IDENTIFIER
|
||||||
|
beq t0, t1, .Lcompile_statement_identifier
|
||||||
|
|
||||||
|
li t1, TOKEN_GOTO
|
||||||
|
beq t0, t1, .Lcompile_statement_goto
|
||||||
|
|
||||||
li t1, TOKEN_RETURN
|
li t1, TOKEN_RETURN
|
||||||
beq t0, t1, .Lcompile_statement_return
|
beq t0, t1, .Lcompile_statement_return
|
||||||
|
|
||||||
@ -1494,10 +1451,6 @@ _compile_statement:
|
|||||||
li t1, TOKEN_DOT
|
li t1, TOKEN_DOT
|
||||||
beq t0, t1, .Lcompile_statement_label
|
beq t0, t1, .Lcompile_statement_label
|
||||||
|
|
||||||
lbu t0, (s1)
|
|
||||||
li t1, '_'
|
|
||||||
beq t0, t1, .Lcompile_statement_identifier
|
|
||||||
|
|
||||||
j .Lcompile_statement_empty # Else.
|
j .Lcompile_statement_empty # Else.
|
||||||
|
|
||||||
.Lcompile_statement_if:
|
.Lcompile_statement_if:
|
||||||
@ -1505,7 +1458,6 @@ _compile_statement:
|
|||||||
j .Lcompile_statement_end
|
j .Lcompile_statement_end
|
||||||
|
|
||||||
.Lcompile_statement_label:
|
.Lcompile_statement_label:
|
||||||
lw a0, 20(sp)
|
|
||||||
call _compile_label
|
call _compile_label
|
||||||
j .Lcompile_statement_end
|
j .Lcompile_statement_end
|
||||||
|
|
||||||
@ -1527,7 +1479,6 @@ _compile_statement:
|
|||||||
|
|
||||||
.Lcompile_statement_end:
|
.Lcompile_statement_end:
|
||||||
sw a0, 12(sp)
|
sw a0, 12(sp)
|
||||||
call _skip_spaces
|
|
||||||
call _skip_comment
|
call _skip_comment
|
||||||
lw a0, 12(sp)
|
lw a0, 12(sp)
|
||||||
|
|
||||||
@ -1559,10 +1510,10 @@ _compile_text_section:
|
|||||||
.type _compile_entry_point, @function
|
.type _compile_entry_point, @function
|
||||||
_compile_entry_point:
|
_compile_entry_point:
|
||||||
# Prologue.
|
# Prologue.
|
||||||
addi sp, sp, -8
|
addi sp, sp, -32
|
||||||
sw ra, 4(sp)
|
sw ra, 28(sp)
|
||||||
sw s0, 0(sp)
|
sw s0, 24(sp)
|
||||||
addi s0, sp, 8
|
addi s0, sp, 32
|
||||||
|
|
||||||
# .type _start, @function
|
# .type _start, @function
|
||||||
la a0, asm_start
|
la a0, asm_start
|
||||||
@ -1581,7 +1532,6 @@ _compile_entry_point:
|
|||||||
li t1, TOKEN_END
|
li t1, TOKEN_END
|
||||||
beq t0, t1, .Lcompile_entry_point_end
|
beq t0, t1, .Lcompile_entry_point_end
|
||||||
|
|
||||||
lw a0, 12(sp)
|
|
||||||
call _compile_statement
|
call _compile_statement
|
||||||
j .Lcompile_entry_point_body
|
j .Lcompile_entry_point_body
|
||||||
|
|
||||||
@ -1593,27 +1543,9 @@ _compile_entry_point:
|
|||||||
call _write_out
|
call _write_out
|
||||||
|
|
||||||
# Epilogue.
|
# Epilogue.
|
||||||
lw ra, 4(sp)
|
lw ra, 28(sp)
|
||||||
lw s0, 0(sp)
|
lw s0, 24(sp)
|
||||||
addi sp, sp, 8
|
addi sp, sp, 32
|
||||||
ret
|
|
||||||
|
|
||||||
# Finds the end of the line and returns its length in a0.
|
|
||||||
.type _read_line, @function
|
|
||||||
_read_line:
|
|
||||||
mv t0, s1 # Local position in the source text.
|
|
||||||
|
|
||||||
.Lread_line_do:
|
|
||||||
lbu t1, (t0) # t1 = Current character.
|
|
||||||
beqz t1, .Lread_line_end # Exit the loop on the NUL character.
|
|
||||||
li t2, '\n'
|
|
||||||
beq t1, t2, .Lread_line_end # Exit the loop on the new line.
|
|
||||||
|
|
||||||
addi t0, t0, 1
|
|
||||||
j .Lread_line_do
|
|
||||||
|
|
||||||
.Lread_line_end:
|
|
||||||
sub a0, t0, s1 # Return the line length.
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
.type _compile, @function
|
.type _compile, @function
|
||||||
|
604
boot/stage2.elna
604
boot/stage2.elna
File diff suppressed because it is too large
Load Diff
@ -266,9 +266,9 @@ transitions:
|
|||||||
.word 0x05ff, 0x0102, 0x05ff, 0x0102, 0x0102, 0x0102, 0x05ff, 0x05ff
|
.word 0x05ff, 0x0102, 0x05ff, 0x0102, 0x0102, 0x0102, 0x05ff, 0x05ff
|
||||||
.word 0x05ff, 0x05ff, 0x05ff, 0x05ff # 0x02 Identifier
|
.word 0x05ff, 0x05ff, 0x05ff, 0x05ff # 0x02 Identifier
|
||||||
|
|
||||||
.word 0x02ff, 0x0103, 0x00ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff
|
.word 0x08ff, 0x0103, 0x00ff, 0x08ff, 0x08ff, 0x08ff, 0x08ff, 0x08ff
|
||||||
.word 0x02ff, 0x02ff, 0x02ff, 0x00ff, 0x0103, 0x02ff, 0x02ff, 0x02ff
|
.word 0x08ff, 0x00ff, 0x08ff, 0x00ff, 0x0103, 0x00ff, 0x08ff, 0x08ff
|
||||||
.word 0x02ff, 0x02ff, 0x02ff, 0x02ff # 0x03 Integer
|
.word 0x08ff, 0x08ff, 0x08ff, 0x08ff # 0x03 Integer
|
||||||
|
|
||||||
.word 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x04ff, 0x02ff, 0x02ff
|
.word 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x04ff, 0x02ff, 0x02ff
|
||||||
.word 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff
|
.word 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff, 0x02ff
|
||||||
@ -518,6 +518,9 @@ _tokenize_next:
|
|||||||
li t0, 0x07 # An action for symbols containing multiple characters.
|
li t0, 0x07 # An action for symbols containing multiple characters.
|
||||||
beq t1, t0, .Ltokenize_next_composite
|
beq t1, t0, .Ltokenize_next_composite
|
||||||
|
|
||||||
|
li t0, 0x08 # Integer action.
|
||||||
|
beq t1, t0, .Ltokenize_next_integer
|
||||||
|
|
||||||
j .Ltokenize_next_reject
|
j .Ltokenize_next_reject
|
||||||
|
|
||||||
.Ltokenize_next_reject:
|
.Ltokenize_next_reject:
|
||||||
@ -588,6 +591,17 @@ _tokenize_next:
|
|||||||
|
|
||||||
j .Ltokenize_next_end
|
j .Ltokenize_next_end
|
||||||
|
|
||||||
|
.Ltokenize_next_integer:
|
||||||
|
lw a1, 12(sp)
|
||||||
|
sub a0, s1, a1
|
||||||
|
sw a0, 8(sp)
|
||||||
|
sw a0, 4(sp)
|
||||||
|
lw a0, 0(sp)
|
||||||
|
addi a1, sp, 4
|
||||||
|
li a2, 12
|
||||||
|
call _memcpy
|
||||||
|
j .Ltokenize_next_end
|
||||||
|
|
||||||
.Ltokenize_next_end:
|
.Ltokenize_next_end:
|
||||||
mv a0, s1 # Return the advanced text pointer.
|
mv a0, s1 # Return the advanced text pointer.
|
||||||
|
|
||||||
|
335
rakelib/cross.rake
Normal file
335
rakelib/cross.rake
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||||
|
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
|
# obtain one at https://mozilla.org/MPL/2.0/. -}
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'pathname'
|
||||||
|
require 'uri'
|
||||||
|
require 'net/http'
|
||||||
|
require 'rake/clean'
|
||||||
|
require 'open3'
|
||||||
|
require 'etc'
|
||||||
|
|
||||||
|
GCC_VERSION = "15.1.0"
|
||||||
|
BINUTILS_VERSION = '2.44'
|
||||||
|
GLIBC_VERSION = '2.41'
|
||||||
|
KERNEL_VERSION = '5.15.181'
|
||||||
|
|
||||||
|
CLOBBER.include 'build'
|
||||||
|
|
||||||
|
class BuildTarget
|
||||||
|
attr_accessor(:build, :gcc, :target, :tmp)
|
||||||
|
|
||||||
|
def gxx
|
||||||
|
@gcc.gsub 'c', '+'
|
||||||
|
end
|
||||||
|
|
||||||
|
def sysroot
|
||||||
|
tmp + 'sysroot'
|
||||||
|
end
|
||||||
|
|
||||||
|
def rootfs
|
||||||
|
tmp + 'rootfs'
|
||||||
|
end
|
||||||
|
|
||||||
|
def tools
|
||||||
|
tmp + 'tools'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def gcc_verbose(gcc_binary)
|
||||||
|
read, write = IO.pipe
|
||||||
|
sh({'LANG' => 'C'}, gcc_binary, '--verbose', err: write)
|
||||||
|
write.close
|
||||||
|
output = read.read
|
||||||
|
read.close
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_build_target(gcc_version, task)
|
||||||
|
gcc_binary = 'gcc'
|
||||||
|
output = gcc_verbose gcc_binary
|
||||||
|
|
||||||
|
if output.start_with? 'Apple clang'
|
||||||
|
gcc_binary = "gcc-#{gcc_version.split('.').first}"
|
||||||
|
output = gcc_verbose gcc_binary
|
||||||
|
end
|
||||||
|
result = output
|
||||||
|
.lines
|
||||||
|
.each_with_object(BuildTarget.new) do |line, accumulator|
|
||||||
|
if line.start_with? 'Target: '
|
||||||
|
accumulator.build = line.split(' ').last.strip
|
||||||
|
elsif line.start_with? 'COLLECT_GCC'
|
||||||
|
accumulator.gcc = line.split('=').last.strip
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result.tmp = Pathname.new('./build')
|
||||||
|
task.with_defaults target: 'riscv32-unknown-linux-gnu'
|
||||||
|
result.target = task[:target]
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def download_and_unarchive(url, target)
|
||||||
|
case File.extname url.path
|
||||||
|
when '.bz2'
|
||||||
|
archive_type = '-j'
|
||||||
|
root_directory = File.basename url.path, '.tar.bz2'
|
||||||
|
when '.xz'
|
||||||
|
archive_type = '-J'
|
||||||
|
root_directory = File.basename url.path, '.tar.xz'
|
||||||
|
else
|
||||||
|
raise "Unsupported archive type #{url.path}."
|
||||||
|
end
|
||||||
|
|
||||||
|
Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http|
|
||||||
|
request = Net::HTTP::Get.new url.request_uri
|
||||||
|
|
||||||
|
http.request request do |response|
|
||||||
|
case response
|
||||||
|
when Net::HTTPRedirection
|
||||||
|
download_and_unarchive URI.parse(response['location'])
|
||||||
|
when Net::HTTPSuccess
|
||||||
|
Open3.popen2 'tar', '-C', target.to_path, archive_type, '-xv' do |stdin, stdout, wait_thread|
|
||||||
|
Thread.new do
|
||||||
|
stdout.each { |line| puts line }
|
||||||
|
end
|
||||||
|
|
||||||
|
response.read_body do |chunk|
|
||||||
|
stdin.write chunk
|
||||||
|
end
|
||||||
|
stdin.close
|
||||||
|
|
||||||
|
wait_thread.value
|
||||||
|
end
|
||||||
|
else
|
||||||
|
response.error!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
target + root_directory
|
||||||
|
end
|
||||||
|
|
||||||
|
namespace :cross do
|
||||||
|
desc 'Build cross binutils'
|
||||||
|
task :binutils, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
source_directory = download_and_unarchive(
|
||||||
|
URI.parse("https://ftp.gnu.org/gnu/binutils/binutils-#{BINUTILS_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
|
||||||
|
cwd = source_directory.dirname + 'build-binutils'
|
||||||
|
cwd.mkpath
|
||||||
|
options.rootfs.mkpath
|
||||||
|
|
||||||
|
env = {
|
||||||
|
'CC' => options.gcc,
|
||||||
|
'CXX' => options.gxx
|
||||||
|
}
|
||||||
|
configure_options = [
|
||||||
|
"--prefix=#{options.rootfs.realpath}",
|
||||||
|
"--target=#{options.target}",
|
||||||
|
'--disable-nls',
|
||||||
|
'--enable-gprofng=no',
|
||||||
|
'--disable-werror',
|
||||||
|
'--enable-default-hash-style=gnu',
|
||||||
|
'--disable-libquadmath'
|
||||||
|
]
|
||||||
|
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build stage 1 GCC'
|
||||||
|
task :gcc1, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
source_directory = download_and_unarchive(
|
||||||
|
URI.parse("https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
|
||||||
|
cwd = source_directory.dirname + 'build-gcc'
|
||||||
|
cwd.mkpath
|
||||||
|
options.rootfs.mkpath
|
||||||
|
options.sysroot.mkpath
|
||||||
|
|
||||||
|
sh 'contrib/download_prerequisites', chdir: source_directory.to_path
|
||||||
|
configure_options = [
|
||||||
|
"--prefix=#{options.rootfs.realpath}",
|
||||||
|
"--with-sysroot=#{options.sysroot.realpath}",
|
||||||
|
'--enable-languages=c,c++',
|
||||||
|
'--disable-shared',
|
||||||
|
'--with-arch=rv32imafdc',
|
||||||
|
'--with-abi=ilp32d',
|
||||||
|
'--with-tune=rocket',
|
||||||
|
'--with-isa-spec=20191213',
|
||||||
|
'--disable-bootstrap',
|
||||||
|
'--disable-multilib',
|
||||||
|
'--disable-libmudflap',
|
||||||
|
'--disable-libssp',
|
||||||
|
'--disable-libquadmath',
|
||||||
|
'--disable-libsanitizer',
|
||||||
|
'--disable-threads',
|
||||||
|
'--disable-libatomic',
|
||||||
|
'--disable-libgomp',
|
||||||
|
'--disable-libvtv',
|
||||||
|
'--disable-libstdcxx',
|
||||||
|
'--disable-nls',
|
||||||
|
'--with-newlib',
|
||||||
|
'--without-headers',
|
||||||
|
"--target=#{options.target}",
|
||||||
|
"--build=#{options.build}",
|
||||||
|
"--host=#{options.build}"
|
||||||
|
]
|
||||||
|
flags = '-O2 -fPIC'
|
||||||
|
env = {
|
||||||
|
'CC' => options.gcc,
|
||||||
|
'CXX' => options.gxx,
|
||||||
|
'CFLAGS' => flags,
|
||||||
|
'CXXFLAGS' => flags,
|
||||||
|
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
|
||||||
|
}
|
||||||
|
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Copy glibc headers'
|
||||||
|
task :headers, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
|
||||||
|
source_directory = download_and_unarchive(
|
||||||
|
URI.parse("https://ftp.gnu.org/gnu/glibc/glibc-#{GLIBC_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
include_directory = options.tools + 'include'
|
||||||
|
|
||||||
|
include_directory.mkpath
|
||||||
|
cp (source_directory + 'elf/elf.h'), (include_directory + 'elf.h')
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build linux kernel'
|
||||||
|
task :kernel, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
|
||||||
|
cwd = download_and_unarchive(
|
||||||
|
URI.parse("https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-#{KERNEL_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
|
||||||
|
env = {
|
||||||
|
'CROSS_COMPILE' => "#{options.target}-",
|
||||||
|
'ARCH' => 'riscv',
|
||||||
|
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}",
|
||||||
|
'HOSTCFLAGS' => "-D_UUID_T -D__GETHOSTUUID_H -I#{options.tools.realpath + 'include'}"
|
||||||
|
}
|
||||||
|
sh env, 'make', 'rv32_defconfig', chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'headers', chdir: cwd.to_path
|
||||||
|
|
||||||
|
user_directory = options.sysroot + 'usr'
|
||||||
|
|
||||||
|
user_directory.mkpath
|
||||||
|
cp_r (cwd + 'usr/include'), (user_directory + 'include')
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build glibc'
|
||||||
|
task :glibc, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
source_directory = options.tools + "glibc-#{GLIBC_VERSION}"
|
||||||
|
configure_options = [
|
||||||
|
'--prefix=/usr',
|
||||||
|
"--host=#{options.target}",
|
||||||
|
"--target=#{options.target}",
|
||||||
|
"--build=#{options.build}",
|
||||||
|
"--enable-kernel=#{KERNEL_VERSION}",
|
||||||
|
"--with-headers=#{options.sysroot.realpath + 'usr/include'}",
|
||||||
|
'--disable-nscd',
|
||||||
|
'--disable-libquadmath',
|
||||||
|
'--disable-libitm',
|
||||||
|
'--disable-werror',
|
||||||
|
'libc_cv_forced_unwind=yes'
|
||||||
|
]
|
||||||
|
bin = options.rootfs.realpath + 'bin'
|
||||||
|
env = {
|
||||||
|
'PATH' => "#{bin}:#{ENV['PATH']}",
|
||||||
|
'MAKE' => 'make' # Otherwise it uses gnumake which can be different and too old.
|
||||||
|
}
|
||||||
|
cwd = source_directory.dirname + 'build-glibc'
|
||||||
|
cwd.mkpath
|
||||||
|
|
||||||
|
configure = source_directory.relative_path_from(cwd) +'./configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', "install_root=#{options.sysroot.realpath}", 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build stage 2 GCC'
|
||||||
|
task :gcc2, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
source_directory = options.tools + "gcc-#{GCC_VERSION}"
|
||||||
|
cwd = options.tools + 'build-gcc'
|
||||||
|
|
||||||
|
rm_rf cwd
|
||||||
|
cwd.mkpath
|
||||||
|
|
||||||
|
configure_options = [
|
||||||
|
"--prefix=#{options.rootfs.realpath}",
|
||||||
|
"--with-sysroot=#{options.sysroot.realpath}",
|
||||||
|
'--enable-languages=c,c++,lto',
|
||||||
|
'--enable-lto',
|
||||||
|
'--enable-shared',
|
||||||
|
'--with-arch=rv32imafdc',
|
||||||
|
'--with-abi=ilp32d',
|
||||||
|
'--with-tune=rocket',
|
||||||
|
'--with-isa-spec=20191213',
|
||||||
|
'--disable-bootstrap',
|
||||||
|
'--disable-multilib',
|
||||||
|
'--enable-checking=release',
|
||||||
|
'--disable-libssp',
|
||||||
|
'--disable-libquadmath',
|
||||||
|
'--enable-threads=posix',
|
||||||
|
'--with-default-libstdcxx-abi=new',
|
||||||
|
'--disable-nls',
|
||||||
|
"--target=#{options.target}",
|
||||||
|
"--build=#{options.build}",
|
||||||
|
"--host=#{options.build}"
|
||||||
|
|
||||||
|
]
|
||||||
|
flags = '-O2 -fPIC'
|
||||||
|
env = {
|
||||||
|
'CFLAGS' => flags,
|
||||||
|
'CXXFLAGS' => flags,
|
||||||
|
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
|
||||||
|
}
|
||||||
|
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
|
||||||
|
task :init, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
env = {
|
||||||
|
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
|
||||||
|
}
|
||||||
|
sh env, 'riscv32-unknown-linux-gnu-gcc',
|
||||||
|
'-ffreestanding', '-static',
|
||||||
|
'-o', (options.tools + 'init').to_path,
|
||||||
|
'tools/init.c'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build cross toolchain'
|
||||||
|
task cross: [
|
||||||
|
'cross:binutils',
|
||||||
|
'cross:gcc1',
|
||||||
|
'cross:headers',
|
||||||
|
'cross:kernel',
|
||||||
|
'cross:glibc',
|
||||||
|
'cross:gcc2',
|
||||||
|
'cross:init'
|
||||||
|
] do
|
||||||
|
end
|
57
rakelib/stage.rake
Normal file
57
rakelib/stage.rake
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||||
|
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
|
# obtain one at https://mozilla.org/MPL/2.0/. -}
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
CROSS_GCC = 'build/rootfs/bin/riscv32-unknown-linux-gnu-gcc'
|
||||||
|
SYSROOT = 'build/sysroot'
|
||||||
|
QEMU = 'qemu-riscv32'
|
||||||
|
|
||||||
|
def assemble_stage(output, compiler, source)
|
||||||
|
arguments = [QEMU, '-L', SYSROOT, *compiler]
|
||||||
|
|
||||||
|
puts Term::ANSIColor.green(arguments * ' ')
|
||||||
|
puts
|
||||||
|
Open3.popen2(*arguments) do |qemu_in, qemu_out|
|
||||||
|
qemu_in.write File.read(*source)
|
||||||
|
qemu_in.close
|
||||||
|
|
||||||
|
IO.copy_stream qemu_out, output
|
||||||
|
qemu_out.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Dir.glob('boot/*.s').each do |assembly_source|
|
||||||
|
target_object = Pathname.new('build/boot') + Pathname.new(assembly_source).basename.sub_ext('.o')
|
||||||
|
|
||||||
|
file target_object.to_s => [assembly_source, 'build/boot'] do |t|
|
||||||
|
sh CROSS_GCC, '-c', '-o', t.name, assembly_source
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Initial stage'
|
||||||
|
file 'build/boot/stage1' => ['build/boot/tokenizer.o', 'build/boot/stage1.o', 'build/boot/common-boot.o'] do |t|
|
||||||
|
sh CROSS_GCC, '-nostdlib', '-o', t.name, *t.prerequisites
|
||||||
|
end
|
||||||
|
|
||||||
|
file 'build/boot/stage2a.s' => ['build/boot/stage1', 'boot/stage2.elna'] do |t|
|
||||||
|
source, exe = t.prerequisites.partition { |prerequisite| prerequisite.end_with? '.elna' }
|
||||||
|
|
||||||
|
File.open t.name, 'w' do |output|
|
||||||
|
assemble_stage output, exe, source
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
['build/boot/stage2a', 'build/boot/stage2b'].each do |exe|
|
||||||
|
file exe => [exe.ext('.s'), 'build/boot/common-boot.o'] do |t|
|
||||||
|
sh CROSS_GCC, '-nostdlib', '-o', t.name, *t.prerequisites
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
file 'build/boot/stage2b.s' => ['build/boot/stage2a', 'boot/stage2.elna'] do |t|
|
||||||
|
source, exe = t.prerequisites.partition { |prerequisite| prerequisite.end_with? '.elna' }
|
||||||
|
|
||||||
|
File.open t.name, 'w' do |output|
|
||||||
|
assemble_stage output, exe, source
|
||||||
|
end
|
||||||
|
end
|
204
tools/init.c
Normal file
204
tools/init.c
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/reboot.h>
|
||||||
|
|
||||||
|
#define FILENAME_BUFFER_SIZE 256
|
||||||
|
|
||||||
|
size_t read_command(int descriptor, char *command_buffer)
|
||||||
|
{
|
||||||
|
ssize_t bytes_read = 0;
|
||||||
|
size_t read_so_far = 0;
|
||||||
|
|
||||||
|
while ((bytes_read = read(descriptor, command_buffer + read_so_far, FILENAME_BUFFER_SIZE - read_so_far - 1)) > 0)
|
||||||
|
{
|
||||||
|
read_so_far += bytes_read;
|
||||||
|
if (read_so_far >= FILENAME_BUFFER_SIZE - 1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command_buffer[read_so_far] = 0;
|
||||||
|
return read_so_far;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum status
|
||||||
|
{
|
||||||
|
status_success,
|
||||||
|
status_failure,
|
||||||
|
status_warning,
|
||||||
|
status_fatal
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int make_path(char *destination, const char *directory, const char *filename, const char *extension)
|
||||||
|
{
|
||||||
|
unsigned int i = 0;
|
||||||
|
|
||||||
|
for (; i < FILENAME_BUFFER_SIZE; i++)
|
||||||
|
{
|
||||||
|
if (directory[i] == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
destination[i] = directory[i];
|
||||||
|
}
|
||||||
|
for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++)
|
||||||
|
{
|
||||||
|
if (filename[j] == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
destination[i] = filename[j];
|
||||||
|
}
|
||||||
|
if (extension == NULL)
|
||||||
|
{
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++)
|
||||||
|
{
|
||||||
|
if (extension[j] == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
destination[i] = extension[j];
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
destination[i] = 0;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum status run_test(const char *file_entry_name)
|
||||||
|
{
|
||||||
|
printf("Running %s. ", file_entry_name);
|
||||||
|
|
||||||
|
char filename[FILENAME_BUFFER_SIZE];
|
||||||
|
char command_buffer[FILENAME_BUFFER_SIZE];
|
||||||
|
char file_buffer[256];
|
||||||
|
int pipe_ends[2];
|
||||||
|
|
||||||
|
if (pipe(pipe_ends) == -1)
|
||||||
|
{
|
||||||
|
perror("pipe");
|
||||||
|
return status_fatal;
|
||||||
|
}
|
||||||
|
make_path(filename, "./tests/", file_entry_name, NULL);
|
||||||
|
|
||||||
|
int child_pid = fork();
|
||||||
|
if (child_pid == -1)
|
||||||
|
{
|
||||||
|
return status_fatal;
|
||||||
|
}
|
||||||
|
else if (child_pid == 0)
|
||||||
|
{
|
||||||
|
close(STDIN_FILENO);
|
||||||
|
close(STDERR_FILENO);
|
||||||
|
close(pipe_ends[0]); // Close the read end.
|
||||||
|
|
||||||
|
if (dup2(pipe_ends[1], STDOUT_FILENO) == -1)
|
||||||
|
{
|
||||||
|
perror("dup2");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
execl(filename, filename);
|
||||||
|
perror("execl");
|
||||||
|
}
|
||||||
|
close(STDOUT_FILENO);
|
||||||
|
close(pipe_ends[1]);
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close(pipe_ends[1]); // Close the write end.
|
||||||
|
read_command(pipe_ends[0], command_buffer);
|
||||||
|
close(pipe_ends[0]);
|
||||||
|
|
||||||
|
int wait_status = 0;
|
||||||
|
|
||||||
|
make_path(filename, "./expectations/", file_entry_name, ".txt");
|
||||||
|
|
||||||
|
FILE *expectation_descriptor = fopen(filename, "r");
|
||||||
|
|
||||||
|
if (expectation_descriptor == NULL)
|
||||||
|
{
|
||||||
|
return status_warning;
|
||||||
|
}
|
||||||
|
size_t read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor);
|
||||||
|
fclose(expectation_descriptor);
|
||||||
|
|
||||||
|
file_buffer[read_from_file] = 0;
|
||||||
|
for (unsigned int i = 0; ; ++i)
|
||||||
|
{
|
||||||
|
if (command_buffer[i] == 0 && file_buffer[i] == 0)
|
||||||
|
{
|
||||||
|
fwrite("\n", 1, 1, stdout);
|
||||||
|
return status_success;
|
||||||
|
}
|
||||||
|
else if (command_buffer[i] != file_buffer[i])
|
||||||
|
{
|
||||||
|
printf("Failed. Got:\n%s", command_buffer);
|
||||||
|
return status_failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct summary
|
||||||
|
{
|
||||||
|
size_t total;
|
||||||
|
size_t failure;
|
||||||
|
size_t success;
|
||||||
|
};
|
||||||
|
|
||||||
|
void walk()
|
||||||
|
{
|
||||||
|
DIR *directory_stream = opendir("./tests");
|
||||||
|
struct dirent *file_entry;
|
||||||
|
|
||||||
|
struct summary test_summary = { .total = 0, .failure = 0, .success = 0 };
|
||||||
|
|
||||||
|
while ((file_entry = readdir(directory_stream)) != NULL)
|
||||||
|
{
|
||||||
|
if (file_entry->d_name[0] == '.')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++test_summary.total;
|
||||||
|
switch (run_test(file_entry->d_name))
|
||||||
|
{
|
||||||
|
case status_failure:
|
||||||
|
++test_summary.failure;
|
||||||
|
break;
|
||||||
|
case status_success:
|
||||||
|
++test_summary.success;
|
||||||
|
break;
|
||||||
|
case status_warning:
|
||||||
|
break;
|
||||||
|
case status_fatal:
|
||||||
|
goto end_walk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("Successful: %lu, Failed: %lu, Total: %lu.\n",
|
||||||
|
test_summary.success, test_summary.failure, test_summary.total);
|
||||||
|
end_walk:
|
||||||
|
closedir(directory_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int dev_console = open("/dev/console", O_WRONLY);
|
||||||
|
if (dev_console != -1)
|
||||||
|
{
|
||||||
|
dup2(dev_console, STDOUT_FILENO);
|
||||||
|
walk();
|
||||||
|
close(dev_console);
|
||||||
|
}
|
||||||
|
sync();
|
||||||
|
reboot(RB_POWER_OFF);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user