[PATCH] Add module-level inline assembly
Export this patch
This patch adds module-level inline assembly. The main idea is to
introduce an "asm" keyword, which takes a string which is echoed
straight into the output file. The only post-processing done on the
string is escaping it.
This feature roughly corresponds to the top-level "asm" macro in most C
compilers or the "global_asm!" macro in Rust. I've added documentation
of this feature to "il.txt" as well.
I have not implemented instruction-level inline assembly, since it is
more controversial and will likely impact more of the codebase.
This is my first patch for this codebase. I may not be aware of some
code idioms.
Signed-off-by: John Nunley <dev@notgull.net>
---
all.h | 2 +-
doc/il.txt | 16 ++++++++++++++++
main.c | 8 +++++++-
parse.c | 37 +++++++++++++++++++++++++++++++++++--
test/global_asm.ssa | 30 ++++++++++++++++++++++++++++++
tools/lexh.c | 2 +-
6 files changed, 90 insertions(+), 5 deletions(-)
create mode 100644 test/global_asm.ssa
diff --git a/all.h b/all.h
index 97cc41c..c4e176d 100644
--- a/all.h
+++ b/all.h
@@ -515,7 +515,7 @@ bshas(BSet *bs, uint elt)
/* parse.c */
extern Op optab[NOp];
-void parse(FILE *, char *, void (char *), void (Dat *), void (Fn *));
+void parse(FILE *, char *, void (char *), void (Dat *), void (Fn *), void (const char *));
void printfn(Fn *, FILE *);
void printref(Ref, Fn *, FILE *);
void err(char *, ...) __attribute__((noreturn));
diff --git a/doc/il.txt b/doc/il.txt
index 7ec5fd0..f37bb3e 100644
--- a/doc/il.txt
+++ b/doc/il.txt
@@ -510,6 +510,22 @@ necessary to compile a call is in the instruction itself.
The syntax and semantics for the body of functions
are described in the <@ Control > section.
+~ Module-Level Inline Assembly
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ `bnf
+ ASM := 'asm' '"' ... '"'
+
+It is often necessary to bypass QBE in order to invoke
+instructions or create assembly code that QBE cannot
+emit. For instance, libc implementations will need to
+invoke a "syscall" instruction, which QBE cannot emit.
+One possible way to invoke these instructions is with
+module-level inline assembly.
+
+The contents of the string are echoed directly to the
+output file that QBE is compiling.
+
- 6. Control
------------
diff --git a/main.c b/main.c
index 5ecb4d0..ebbf067 100644
--- a/main.c
+++ b/main.c
@@ -113,6 +113,12 @@ dbgfile(char *fn)
emitdbgfile(fn, outf);
}
+static void
+glasm(const char *text)
+{
+ fprintf(outf, "%s", text);
+}
+
int
main(int ac, char *av[])
{
@@ -187,7 +193,7 @@ main(int ac, char *av[])
exit(1);
}
}
- parse(inf, f, dbgfile, data, func);
+ parse(inf, f, dbgfile, data, func, glasm);
fclose(inf);
} while (++optind < ac);
diff --git a/parse.c b/parse.c
index e896679..9e93fd3 100644
--- a/parse.c
+++ b/parse.c
@@ -41,6 +41,7 @@ enum Token {
Talloc1,
Talloc2,
+ Tasm,
Tblit,
Tcall,
Tenv,
@@ -100,6 +101,7 @@ static char *kwmap[Ntok] = {
[Tloadd] = "loadd",
[Talloc1] = "alloc1",
[Talloc2] = "alloc2",
+ [Tasm] = "asm",
[Tblit] = "blit",
[Tcall] = "call",
[Tenv] = "env",
@@ -137,7 +139,7 @@ enum {
TMask = 16383, /* for temps hash */
BMask = 8191, /* for blocks hash */
- K = 11183273, /* found using tools/lexh.c */
+ K = 15286327, /* found using tools/lexh.c */
M = 23,
};
@@ -1198,8 +1200,36 @@ parselnk(Lnk *lnk)
}
}
+static void
+parseglasm(void glasm(const char *))
+{
+ const char *t;
+ char *scont, *p;
+ int len;
+
+ expect(Tstr);
+ t = tokval.str;
+ assert(*t++ == '"');
+
+ len = strlen(t);
+ scont = malloc(len);
+ if (!scont)
+ die("unable to allocate memory");
+ memcpy(scont, t, len);
+
+ assert(scont[len-1] == '"');
+ scont[len-1] = '\0';
+
+ p = strtok(scont, "\\");
+ do glasm(p);
+ while ((p = strtok(NULL, "\\")));
+
+ glasm("\n");
+ free(scont);
+}
+
void
-parse(FILE *f, char *path, void dbgfile(char *), void data(Dat *), void func(Fn *))
+parse(FILE *f, char *path, void dbgfile(char *), void data(Dat *), void func(Fn *), void glasm(const char *))
{
Lnk lnk;
uint n;
@@ -1220,6 +1250,9 @@ parse(FILE *f, char *path, void dbgfile(char *), void data(Dat *), void func(Fn
expect(Tstr);
dbgfile(tokval.str);
break;
+ case Tasm:
+ parseglasm(glasm);
+ break;
case Tfunc:
lnk.align = 16;
func(parsefn(&lnk));
diff --git a/test/global_asm.ssa b/test/global_asm.ssa
new file mode 100644
index 0000000..4c25bed
--- /dev/null
+++ b/test/global_asm.ssa
@@ -0,0 +1,30 @@
+# tests global assembly
+
+asm "
+.data
+.balign 8
+x:
+ .ascii \"foobar\"
+
+.text
+.balign 16
+.globl asm_function
+asm_function:
+ ret
+.type asm_function, @function
+.size asm_function, .-asm_function
+"
+
+export function $call_it() {
+@start
+ call $asm_function ()
+ ret
+}
+
+# >>> driver
+# extern void call_it();
+# int main() {
+# call_it();
+# return 0;
+# }
+# <<<
diff --git a/tools/lexh.c b/tools/lexh.c
index efc30fe..fea2b5e 100644
--- a/tools/lexh.c
+++ b/tools/lexh.c
@@ -25,7 +25,7 @@ char *tok[] = {
"cgtd", "cged", "cned", "ceqd", "cod", "cuod",
"vaarg", "vastart", "...", "env", "dbgloc",
- "call", "phi", "jmp", "jnz", "ret", "hlt", "export",
+ "asm", "call", "phi", "jmp", "jnz", "ret", "hlt", "export",
"function", "type", "data", "section", "align", "dbgfile",
"blit", "l", "w", "sh", "uh", "h", "sb", "ub", "b",
"d", "s", "z", "loadw", "loadl", "loads", "loadd",
--
2.34.1