Skip to content

Commit 2fdbf3e

Browse files
committed
Add compilation for x86 macOS
1 parent 4f71a9e commit 2fdbf3e

3 files changed

Lines changed: 117 additions & 35 deletions

File tree

src/gen.cpp

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <iostream>
12
#include "gen.hpp"
23
#include "ast.hpp"
34
#include "errors.hpp"
@@ -12,6 +13,15 @@
1213

1314
namespace av {
1415

16+
Platform platform_from_string(const std::string &s) {
17+
if (s == "macos") {
18+
return MacOS;
19+
} else if (s == "linux") {
20+
return Linux;
21+
}
22+
throw std::runtime_error("unknown platform: " + s);
23+
}
24+
1525
int memory_needed(const Type &type) {
1626
static std::array<int, __count_Type> a({1, 2, 4, 8, 8, 8, 8, 8, 8, 0});
1727
return a[int(type)];
@@ -126,34 +136,29 @@ int maximum_memory(Node *t) {
126136
std::array compiler_intrinsics{
127137
"write", "read", "alloca"
128138
};
139+
140+
std::array<std::array<int, 3>, 2> syscall_numbers = {{
141+
{1, 0, -1},
142+
{33554436, 33554435, -1}
143+
}};
129144
// clang-format on
130-
std::array intrinsic_code{
131-
R"(write:
132-
mov eax, 1
133-
syscall
134-
ret
135-
)",
136-
R"(read:
137-
xor eax, eax
138-
syscall
139-
ret
140-
)",
141-
R"(alloca:
142-
pop rcx
143-
add rdi, 15
144-
and rdi, -16
145-
sub rsp, rdi
146-
mov rax, rsp
147-
push rcx
148-
ret
149-
)",
150-
};
145+
146+
std::string get_intrinsic(const std::string &name, Platform platform) {
147+
if (name == "write") {
148+
return "write:\n mov eax, " + std::to_string(syscall_numbers[int(platform)][0]) + "\n syscall\n ret\n";
149+
} else if (name == "read") {
150+
return "read:\n mov eax, " + std::to_string(syscall_numbers[int(platform)][1]) + "\n syscall\n ret\n";
151+
} else if (name == "alloca") {
152+
return "alloca:\n pop rcx\n add rdi, 15\n and rdi, -16\n sub rsp, rdi\n mov rax, rsp\n push rcx\n ret\n";
153+
}
154+
assert(false);
155+
}
151156

152157
bool is_intrinsic(const std::string &s) {
153158
return std::find(compiler_intrinsics.begin(), compiler_intrinsics.end(), s) != compiler_intrinsics.end();
154159
}
155160

156-
void generate(Node *t, std::ostream &os) {
161+
void generate(Node *t, Platform platform, std::ostream &os) {
157162
std::map<std::string, std::pair<FunctionDecl *, FunctionBody *>> funcs;
158163
std::map<std::string, Type> vars;
159164
std::map<std::string, std::string> func_label;
@@ -588,7 +593,7 @@ void generate(Node *t, std::ostream &os) {
588593
std::sort(used_intrinsics.begin(), used_intrinsics.end());
589594
used_intrinsics.erase(std::unique(used_intrinsics.begin(), used_intrinsics.end()), used_intrinsics.end());
590595
for (std::string &i : used_intrinsics) {
591-
os << intrinsic_code[std::find(compiler_intrinsics.begin(), compiler_intrinsics.end(), i) - compiler_intrinsics.begin()];
596+
os << get_intrinsic(i, platform);
592597
}
593598
}
594599

src/gen.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,18 @@
55

66
namespace av {
77

8+
enum Platform {
9+
Linux,
10+
MacOS
11+
};
12+
13+
Platform platform_from_string(const std::string &s);
14+
815
int memory_needed(const Type &type);
916

1017
// this dfs just calculates the maximum memory we'd need by essentially finding the depth of the ast
1118
int maximum_memory(Node *t);
1219

13-
void generate(Node *t, std::ostream &os);
20+
void generate(Node *t, Platform platform, std::ostream &os);
1421

1522
} // namespace av

src/main.cpp

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,29 @@ bool command_exists(const std::string &cmd) {
1616
return std::system(test.c_str()) == 0;
1717
}
1818

19+
std::string platform() {
20+
#ifdef __APPLE__
21+
return "macos";
22+
#elif _WIN32
23+
return "windows"
24+
#elif __linux__
25+
return "linux";
26+
#endif
27+
return "unknown";
28+
}
29+
30+
std::string def() {
31+
std::string p = platform();
32+
if (p != "linux" && p != "macos") {
33+
p = "linux";
34+
}
35+
return p;
36+
}
37+
38+
std::string white(const std::string &s) { return "\033[1m" + s + "\033[0m"; }
39+
std::string red(const std::string &s) { return "\033[1;31m" + s + "\033[0m"; }
40+
std::string blue(const std::string &s) { return "\033[1;36m" + s + "\033[0m"; }
41+
1942
void print_help(const std::string &name) {
2043
std::cout << "Usage: " << name << " <filename.av>\n";
2144
std::cout << "Options:\n";
@@ -25,6 +48,9 @@ void print_help(const std::string &name) {
2548
std::cout << " -ast: Prints the AST (without desugaring)\n";
2649
std::cout << " --ast: Prints the AST (after desugaring)\n";
2750
std::cout << " -s: Prints the asm\n";
51+
std::cout << "\n";
52+
std::cout << "Parameters:\n";
53+
std::cout << " -target=<platform>: default: " << white(def()) << ", can be either linux or macos\n";
2854
}
2955

3056
void print_version() {
@@ -38,10 +64,6 @@ void print_version() {
3864
#endif
3965
}
4066

41-
std::string white(const std::string &s) { return "\033[1;37m" + s + "\033[0m"; }
42-
std::string red(const std::string &s) { return "\033[1;31m" + s + "\033[0m"; }
43-
std::string blue(const std::string &s) { return "\033[1;36m" + s + "\033[0m"; }
44-
4567
std::vector<std::string> lines;
4668
void print_error(std::ostream &os, std::string file, int r1, int c1, int r2, int c2, std::string err) {
4769
std::string l = std::to_string(r1);
@@ -76,6 +98,15 @@ void print_error(std::ostream &os, std::string file, int r1, int c1, int r2, int
7698
}
7799
}
78100

101+
std::string nasm_platform(const std::string &platform) {
102+
if (platform == "linux") {
103+
return "elf64";
104+
} else if (platform == "macos") {
105+
return "macho64";
106+
}
107+
assert(false);
108+
}
109+
79110
int main(int argc, char **_argv) {
80111
std::vector<std::string> argv(argc);
81112
for (int i = 0; i < argc; ++i) {
@@ -87,17 +118,41 @@ int main(int argc, char **_argv) {
87118
return 0;
88119
}
89120

90-
std::vector<std::string> files;
121+
std::vector<bool> used(argc);
91122
std::array<std::string, 6> options{"-tokenize", "-ast", "-s", "--help", "--version", "--ast"};
92123
std::array values{false, false, false, false, false, false};
93124
for (int i = 1; i < argc; ++i) {
94125
int it = std::find(options.begin(), options.end(), argv[i]) - options.begin();
95126
if (it != options.size()) {
127+
used[i] = true;
96128
if (values[it]) {
97129
std::cerr << "warning: " << options[it] << " repeated\n";
98130
}
99131
values[it] = true;
100-
} else {
132+
}
133+
}
134+
135+
std::array<std::string, 1> params{"-target="};
136+
std::array<std::string, 1> choices = {};
137+
for (int i = 1; i < argc; ++i) {
138+
int it = std::find_if(params.begin(), params.end(), [&](const std::string &s) { return argv[i].find(s) == 0; }) - params.begin();
139+
if (it == params.size()) {
140+
continue;
141+
}
142+
used[i] = true;
143+
if (!choices[it].empty()) {
144+
std::cerr << red("error: ") << "parameter " << params[it].substr(0, params[it].length() - 1) << " specified more than 1 time\n";
145+
return 1;
146+
}
147+
choices[it] = argv[i].substr(params[it].length());
148+
}
149+
if (choices[0].empty()) {
150+
choices[0] = def();
151+
}
152+
153+
std::vector<std::string> files;
154+
for (int i = 1; i < argc; ++i) {
155+
if (!used[i]) {
101156
files.push_back(argv[i]);
102157
}
103158
}
@@ -111,6 +166,11 @@ int main(int argc, char **_argv) {
111166
return 0;
112167
}
113168

169+
if (choices[0] != "linux" && choices[0] != "macos") {
170+
std::cerr << red("error: ") << "value " << choices[0] << " for parameter " << params[0] << " is invalid\n";
171+
return 1;
172+
}
173+
114174
if (files.size() == 0) {
115175
std::cerr << red("error: ") << "no input file specified\n";
116176
return 1;
@@ -213,7 +273,7 @@ int main(int argc, char **_argv) {
213273
av::tokenizer tk(code);
214274
av::Node *root = av::parse(tk);
215275
av::desugar(root);
216-
av::generate(root, std::cout);
276+
av::generate(root, av::platform_from_string(choices[0]), std::cout);
217277
delete root;
218278
return 0;
219279
} catch (av::Error e) {
@@ -238,12 +298,16 @@ int main(int argc, char **_argv) {
238298
}
239299
std::stringstream os;
240300
os << "section .text\n";
241-
av::generate(root, os);
301+
av::generate(root, av::platform_from_string(choices[0]), os);
242302
os << "global _start\n";
243303
os << "_start:\n";
244304
os << " call main\n";
245305
os << " mov edi, eax\n";
246-
os << " mov eax, 60\n";
306+
if (choices[0] == "linux") {
307+
os << " mov eax, 60\n";
308+
} else {
309+
os << " mov eax, 33554433\n";
310+
}
247311
os << " syscall\n";
248312
std::ofstream oss(argv[1] + ".asm");
249313
oss << os.str();
@@ -260,11 +324,17 @@ int main(int argc, char **_argv) {
260324
if (!command_exists("nasm")) {
261325
std::cerr << "warning: nasm is not installed, stopping at the assembly emission step\n";
262326
} else {
263-
std::system(("nasm -f elf64 " + argv[1] + ".asm -o " + argv[1] + ".o").c_str());
327+
std::system(("nasm -f " + nasm_platform(choices[0]) + " " + argv[1] + ".asm -o " + argv[1] + ".o").c_str());
264328
if (!command_exists("ld")) {
265329
std::cerr << "warning: ld is not installed, stopping at the object linking step\n";
266330
} else {
267-
std::system(("ld " + argv[1] + ".o -o a.out").c_str());
331+
if (choices[0] == "linux") {
332+
std::system(("ld " + argv[1] + ".o -o a.out").c_str());
333+
} else if (choices[0] == "macos") {
334+
std::system(("ld -platform_version macos 13.0 13.0 -e _start " + argv[1] + ".o -o a.out 2>/dev/null").c_str());
335+
} else {
336+
assert(false);
337+
}
268338
}
269339
}
270340
}

0 commit comments

Comments
 (0)