Skip to content

Commit 9712213

Browse files
committed
x64 machine code generation (ELF)
1 parent ab91733 commit 9712213

File tree

13 files changed

+564
-15
lines changed

13 files changed

+564
-15
lines changed

examples/x64/hello_world.v

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fn main() {
2+
for _ in 0..5 {
3+
println('Hello world from V x64 machine code generator!')
4+
}
5+
println('Hello again!')
6+
}

v.v

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,31 +95,30 @@ fn main() {
9595
//println('unknown command/argument\n')
9696
//println(compiler.help_text)
9797
}
98-
9998
// Construct the V object from command line arguments
10099
mut v := compiler.new_v(args)
101100
if v.pref.is_verbose {
102101
println(args)
103102
}
104-
105103
if 'run' in args {
106104
// always recompile for now, too error prone to skip recompilation otherwise
107105
// for example for -repl usage, especially when piping lines to v
108106
v.compile()
109107
v.run_compiled_executable_and_exit()
110108
}
111-
112109
mut tmark := benchmark.new_benchmark()
113-
v.compile()
110+
if v.pref.x64 {
111+
v.compile_x64()
112+
} else {
113+
v.compile()
114+
}
114115
if v.pref.is_stats {
115116
tmark.stop()
116117
println( 'compilation took: ' + tmark.total_duration().str() + 'ms')
117118
}
118-
119119
if v.pref.is_test {
120120
v.run_compiled_executable_and_exit()
121121
}
122-
123122
v.finalize_compilation()
124123
}
125124

vlib/builtin/bare/string_bare.v

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ pub fn tos3(s *C.char) string {
5353
}
5454
}
5555

56+
pub fn println(s string) {
57+
58+
}
59+
5660
/*
5761
pub fn (a string) clone() string {
5862
mut b := string {

vlib/compiler/fn.v

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,10 @@ fn (p mut Parser) fn_decl() {
283283
// C function header def? (fn C.NSMakeRect(int,int,int,int))
284284
is_c := f.name == 'C' && p.tok == .dot
285285
// Just fn signature? only builtin.v + default build mode
286-
if p.is_vh {
287-
//println('\n\nfn_decl() name=$f.name receiver_typ=$receiver_typ nogen=$p.cgen.nogen')
288-
}
286+
//if p.is_vh {
287+
//if f.name == 'main' {
288+
//println('\n\nfn_decl() name=$f.name pass=$p.pass $p.file_name receiver_typ=$receiver_typ nogen=$p.cgen.nogen')
289+
//}
289290
if is_c {
290291
p.check(.dot)
291292
f.name = p.check_name()
@@ -369,7 +370,7 @@ fn (p mut Parser) fn_decl() {
369370
//p.fgen_nl()
370371
}
371372
// Register ?option type for return value and args
372-
if typ.starts_with('Option_') {
373+
if typ.starts_with('Option_') {
373374
p.cgen.typedefs << 'typedef Option $typ;'
374375
}
375376
for arg in f.args {
@@ -989,6 +990,12 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
989990
if clone {
990991
p.gen('/*YY f=$f.name arg=$arg.name is_moved=$arg.is_moved*/string_clone(')
991992
}
993+
994+
// x64 println gen
995+
if p.pref.x64 && i == 0 && f.name == 'println' && p.tok == .str && p.peek() == .rpar {
996+
p.x64.gen_print(p.lit)
997+
}
998+
992999
mut typ := p.bool_expression()
9931000
// Register an interface type usage:
9941001
// fn run(r Animal) { ... }
@@ -1008,6 +1015,8 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
10081015
if clone {
10091016
p.gen(')')
10101017
}
1018+
1019+
10111020
// Optimize `println`: replace it with `printf` to avoid extra allocations and
10121021
// function calls.
10131022
// `println(777)` => `printf("%d\n", 777)`
@@ -1021,6 +1030,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) {
10211030
if i == 0 && (f.name == 'println' || f.name == 'print') &&
10221031
!(typ in ['string', 'ustring', 'void' ])
10231032
{
1033+
//
10241034
T := p.table.find_type(typ)
10251035
$if !windows {
10261036
$if !js {
@@ -1543,7 +1553,7 @@ fn (p &Parser) fn_signature_v(f &Fn) string {
15431553
if f.is_method {
15441554
receiver_arg := f.args[0]
15451555
receiver_type := receiver_arg.typ.trim('*')
1546-
f_name = f_name.all_after('${receiver_type}_')
1556+
f_name = f_name.all_after('${receiver_type}_')
15471557
mut rcv_typ := receiver_arg.typ.replace('array_', '[]').replace('map_', 'map[string]')
15481558
if receiver_arg.is_mut { rcv_typ = 'mut '+rcv_typ.trim('*') }
15491559
else if rcv_typ.ends_with('*') || receiver_arg.ptr { rcv_typ = '&'+rcv_typ.trim_right('&*') }

vlib/compiler/for.v

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ fn (p mut Parser) for_st() {
1111
next_tok := p.peek()
1212
//debug := p.scanner.file_path.contains('r_draw')
1313
p.open_scope()
14+
mut label := 0
15+
mut to := 0
1416
if p.tok == .lcbr {
1517
// Infinite loop
1618
p.gen('while (1) {')
@@ -130,9 +132,17 @@ fn (p mut Parser) for_st() {
130132
if is_range {
131133
p.check_types(typ, 'int')
132134
p.check_space(.dotdot)
135+
if p.pref.x64 {
136+
to = p.lit.int()
137+
}
133138
range_typ, range_expr := p.tmp_expr()
134139
p.check_types(range_typ, 'int')
135140
range_end = range_expr
141+
if p.pref.x64 {
142+
label = p.x64.gen_loop_start(expr.int())
143+
//to = range_expr.int() // TODO why empty?
144+
}
145+
136146
}
137147
is_arr := typ.contains('array')
138148
is_str := typ == 'string'
@@ -189,5 +199,8 @@ fn (p mut Parser) for_st() {
189199
p.close_scope()
190200
p.for_expr_cnt--
191201
p.returns = false // TODO handle loops that are guaranteed to return
202+
if label > 0 {
203+
p.x64.gen_loop_end(to, label)
204+
}
192205
}
193206

vlib/compiler/gen_x64.v

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module compiler
2+
3+
import filepath
4+
5+
//import compiler.x64
6+
7+
pub fn (v mut V) compile_x64() {
8+
$if !linux {
9+
println('v -x64 can only generate Linux binaries for now')
10+
println('You are not on a Linux system, so you will not ' +
11+
'be able to run the resulting executable')
12+
}
13+
14+
v.files << v.v_files_from_dir(filepath.join(v.pref.vlib_path, 'builtin', 'bare'))
15+
v.files << v.dir
16+
v.x64.generate_elf_header()
17+
for f in v.files {
18+
v.parse(f, .decl)
19+
}
20+
for f in v.files {
21+
v.parse(f, .main)
22+
}
23+
v.x64.generate_elf_footer()
24+
25+
}

vlib/compiler/main.v

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
os
99
strings
1010
filepath
11+
compiler.x64
1112
)
1213

1314
pub const (
@@ -63,6 +64,7 @@ pub mut:
6364
dir string // directory (or file) being compiled (TODO rename to path?)
6465
table &Table // table with types, vars, functions etc
6566
cgen &CGen // C code generator
67+
x64 &x64.Gen
6668
pref &Preferences // all the preferences and settings extracted to a struct for reusability
6769
lang_dir string // "~/code/v"
6870
out_name string // "program.exe"
@@ -123,6 +125,7 @@ pub mut:
123125

124126
vlib_path string
125127
vpath string
128+
x64 bool
126129
}
127130

128131
// Should be called by main at the end of the compilation process, to cleanup
@@ -968,9 +971,12 @@ pub fn new_v(args[]string) &V {
968971
mut out_name_c := get_vtmp_filename(out_name, '.tmp.c')
969972

970973
cflags := get_cmdline_cflags(args)
971-
972974
rdir := os.realpath(dir)
973975
rdir_name := os.filename(rdir)
976+
977+
if '-bare' in args {
978+
verror('use -freestanding instead of -bare')
979+
}
974980

975981
obfuscate := '-obf' in args
976982
is_repl := '-repl' in args
@@ -998,7 +1004,8 @@ pub fn new_v(args[]string) &V {
9981004
compress: '-compress' in args
9991005
enable_globals: '--enable-globals' in args
10001006
fast: '-fast' in args
1001-
is_bare: '-bare' in args
1007+
is_bare: '-freestanding' in args
1008+
x64: '-x64' in args
10021009
is_repl: is_repl
10031010
build_mode: build_mode
10041011
cflags: cflags
@@ -1028,6 +1035,7 @@ pub fn new_v(args[]string) &V {
10281035
table: new_table(obfuscate)
10291036
out_name_c: out_name_c
10301037
cgen: new_cgen(out_name_c)
1038+
x64: x64.new_gen(out_name)
10311039
vroot: vroot
10321040
pref: pref
10331041
mod: mod

vlib/compiler/parser.v

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module compiler
77
import (
88
os
99
strings
10+
compiler.x64
1011
)
1112

1213
struct Parser {
@@ -28,6 +29,7 @@ mut:
2829
prev_tok2 TokenKind // TODO remove these once the tokens are cached
2930
lit string
3031
cgen &CGen
32+
x64 &x64.Gen
3133
table &Table
3234
import_table ImportTable // Holds imports for just the file being parsed
3335
pass Pass
@@ -161,6 +163,7 @@ fn (v mut V) new_parser(scanner &Scanner) Parser {
161163
table: v.table
162164
cur_fn: EmptyFn
163165
cgen: v.cgen
166+
x64: v.x64
164167
pref: v.pref
165168
os: v.os
166169
vroot: v.vroot
@@ -2967,3 +2970,7 @@ fn (p mut Parser) is_expr_fn_call(start_tok_idx int) (bool, string) {
29672970
}
29682971
return is_fn_call, expr
29692972
}
2973+
2974+
fn todo_remove() {
2975+
x64.new_gen('f')
2976+
}

vlib/compiler/struct.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,8 @@ fn (p mut Parser) struct_init(typ string) string {
299299
continue
300300
}
301301
field_typ := field.typ
302-
if !p.builtin_mod && field_typ.ends_with('*') && field_typ.contains('Cfg') {
303-
p.error('pointer field `${typ}.${field.name}` must be initialized')
302+
if !p.builtin_mod && field_typ.ends_with('*') && p.mod != 'os' { //&&
303+
p.warn('pointer field `${typ}.${field.name}` must be initialized')
304304
}
305305
// init map fields
306306
if field_typ.starts_with('map_') {

vlib/compiler/x64/elf.v

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright (c) 2019 Alexander Medvednikov. All rights reserved.
2+
// Use of this source code is governed by an MIT license
3+
// that can be found in the LICENSE file.
4+
5+
module x64
6+
7+
import os
8+
9+
const (
10+
mag0 = 0x7f
11+
mag1 = `E`
12+
mag2 = `L`
13+
mag3 = `F`
14+
ei_class = 4
15+
elfclass64 = 2
16+
elfdata2lsb = 1
17+
18+
ev_current = 1
19+
elf_osabi = 0
20+
21+
// ELF file types
22+
et_rel = 1
23+
et_exec = 2
24+
et_dyn = 3
25+
26+
e_machine = 0x3e
27+
28+
shn_xindex = 0xffff
29+
30+
sht_null = 0
31+
)
32+
33+
const (
34+
segment_start = 0x400000
35+
)
36+
37+
38+
pub fn (g mut Gen) generate_elf_header() {
39+
g.buf << [byte(mag0), mag1, mag2, mag3]
40+
g.buf << elfclass64 // file class
41+
g.buf << elfdata2lsb // data encoding
42+
g.buf << ev_current // file version
43+
g.buf << 1//elf_osabi
44+
g.write64(0)//et_rel) // et_rel for .o
45+
g.write16(2) // e_type
46+
g.write16(e_machine) //
47+
g.write32(ev_current) // e_version
48+
eh_size := 0x40
49+
phent_size := 0x38
50+
g.write64(segment_start + eh_size + phent_size) // e_entry
51+
g.write64(0x40) // e_phoff
52+
g.write64(0) // e_shoff
53+
g.write32(0) // e_flags
54+
g.write16(eh_size) // e_ehsize
55+
g.write16(phent_size) // e_phentsize
56+
g.write16(1) // e_phnum
57+
g.write16(0) // e_shentsize
58+
g.write16(0) // e_shnum (number of sections)
59+
g.write16(0) // e_shstrndx
60+
// Elf64_Phdr
61+
g.write32(1) // p_type
62+
g.write32(5) // p_flags
63+
g.write64(0) // p_offset
64+
g.write64(segment_start) // p_vaddr addr:050
65+
g.write64(segment_start) //
66+
g.file_size_pos = g.buf.len
67+
g.write64(0) // p_filesz PLACEHOLDER, set to file_size later // addr: 060
68+
g.write64(0) // p_memsz
69+
g.write64(0x1000) // p_align
70+
// user code starts here at
71+
// address: 00070 and a half
72+
}
73+
74+
pub fn (g mut Gen) generate_elf_footer() {
75+
// Return 0
76+
g.mov(.edi, 0) // ret value
77+
g.mov(.eax, 60)
78+
g.syscall()
79+
// Strings table
80+
// Loop thru all strings and set the right addresses
81+
for i, s in g.strings {
82+
g.write64_at(segment_start + g.buf.len, int(g.str_pos[i]))
83+
g.write_string(s)
84+
g.write8(6)
85+
}
86+
// Now we know the file size, set it
87+
file_size := g.buf.len
88+
g.write64_at(file_size, g.file_size_pos) // set file size 64 bit value
89+
g.write64_at(file_size, g.file_size_pos+8)
90+
// Create the binary
91+
f := os.create('out.bin') or { panic(err) }
92+
f.write_bytes(g.buf.data, g.buf.len)
93+
f.close()
94+
println('x64 elf binary has been successfully generated')
95+
}
96+

0 commit comments

Comments
 (0)