Compare commits
96 Commits
9c89bfbdaf
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f406b2a032 | ||
|
|
7a8b14308b | ||
|
|
fee8ea5cb3 | ||
|
|
2aa64f4a37 | ||
|
|
8c164a3b09 | ||
|
|
8c4754b563 | ||
|
|
9d975eeceb | ||
|
|
ecab77f9e9 | ||
|
|
132cedff09 | ||
|
|
f978b66662 | ||
|
|
692ed4d21c | ||
|
|
c04c1a97c9 | ||
|
|
104ba53b33 | ||
|
|
2d56f124a9 | ||
|
|
fc8d65f893 | ||
|
|
2049826870 | ||
|
|
fc16c077d9 | ||
|
|
8fc5521601 | ||
|
|
dfbadfecb9 | ||
|
|
b21ce51435 | ||
|
|
2f67a6f109 | ||
|
|
1177462bda | ||
|
|
246f7a3b71 | ||
|
|
2188448b19 | ||
|
|
d46b104d5c | ||
|
|
07e3604a8c | ||
|
|
fb75434cba | ||
|
|
7569420fe0 | ||
|
|
fb6fd76d0b | ||
|
|
e80f6643dc | ||
|
|
8d8d1cf067 | ||
|
|
1708faf14d | ||
|
|
14ab1f432d | ||
|
|
f03e8517df | ||
|
|
273e6d1058 | ||
|
|
251d24fb30 | ||
|
|
cdcc1f6d3d | ||
|
|
2771609049 | ||
|
|
d0262c586e | ||
|
|
13333c971a | ||
|
|
b4272a67d1 | ||
|
|
6b7fefc5e7 | ||
|
|
f9fe031532 | ||
|
|
7f855f3931 | ||
|
|
5196026ed1 | ||
|
|
5755e243a9 | ||
|
|
67fb9e5b52 | ||
|
|
012d18cdf8 | ||
|
|
81b1010453 | ||
|
|
fb4849d382 | ||
|
|
5455e1cebb | ||
|
|
6b3f4ac486 | ||
|
|
db08fcbd27 | ||
|
|
5c5187f0ff | ||
|
|
0a29b5e6b2 | ||
|
|
9adcfd2ff5 | ||
|
|
20197ee0bc | ||
|
|
f3b6f81ef5 | ||
|
|
109711e79f | ||
|
|
b204860b2e | ||
|
|
a39bf40755 | ||
|
|
6844b2d0b7 | ||
|
|
923ec25d79 | ||
|
|
438c3b3467 | ||
|
|
6ec68a766f | ||
|
|
1c4b5a5095 | ||
|
|
13f43cadeb | ||
|
|
0d808de34c | ||
|
|
56c10daaa7 | ||
|
|
2c6033e501 | ||
|
|
f349fc1d56 | ||
|
|
9dc5bddfef | ||
|
|
87a07e29d6 | ||
|
|
9ceb061ad4 | ||
|
|
fdfc6606cd | ||
|
|
ff962b6361 | ||
|
|
6968777385 | ||
|
|
5b3991e81c | ||
|
|
17a0c9d902 | ||
|
|
390c4c954d | ||
|
|
8caadf9af1 | ||
|
|
d6d2b228ef | ||
|
|
6789984bd1 | ||
|
|
fe0baa26a0 | ||
|
|
9e04938065 | ||
|
|
ba6f9fb6c1 | ||
|
|
07f6d57aba | ||
|
|
012320569e | ||
|
|
fa40a78546 | ||
|
|
8e4cb71924 | ||
|
|
77a459ffd3 | ||
|
|
5ec2349336 | ||
|
|
55bfa2289e | ||
|
|
a1077f7c03 | ||
|
|
83e0771f2c | ||
|
|
4d182e1685 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,9 +1,9 @@
|
|||||||
a.out
|
a.out
|
||||||
tests/*.o
|
|
||||||
tests/*.asm
|
tests/*.asm
|
||||||
ntc
|
ntc
|
||||||
ntc.exe
|
ntc.exe
|
||||||
*.o
|
*.o
|
||||||
|
*.d
|
||||||
*.err
|
*.err
|
||||||
dos4gw.exe
|
dos4gw.exe
|
||||||
massif.out.*
|
massif.out.*
|
||||||
|
|||||||
390
DOCUMENTATION.md
Normal file
390
DOCUMENTATION.md
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
# Nectar Reference Compiler Source Documentation
|
||||||
|
|
||||||
|
<img src="https://mid.net.ua/git/mid/nctref/raw/branch/master/documentation_passes.svg" width="256" align="right" />
|
||||||
|
|
||||||
|
When writing a program, I usually make the most primitive and smallest code I can that does the job. If it turns out I miscalculated the complexity, or I must add some feature that isn't compatible with the codebase, I'll obviously have to refactor it. Still, I've been programming this way for probably my entire life.
|
||||||
|
|
||||||
|
That being said, if you know this compiler took since 2019 to get to its current state, you will correctly guess that I DO NOT KNOW WHAT I AM DOING. Compiler literature and online discussion is abstract to the point where it is not useful for real-world archs. When it gets specific, it's often too simplistic. It's common to say instruction selection should happen before register allocation, but how can you know which instructions to emit when some of them only work with specific registers? People say spilling causes load and store instructions, but that's false -- x86 has memory operands! Imagine how long it took me to realize real-world IRs are not at all generic, and are actually quite close to their target architectures. As a result, much of what you see in the source is me basically banging rocks. There's definitely better ways to do the things I show here, but I figured it's better to have at least some resource on how a "real" compiler works.
|
||||||
|
|
||||||
|
The core idea behind the compiler is to progressively iterate through the AST, turning it into a more primitive form step by step. After this process, the code generator is given the code in a form it will understand. Doing it this way is necessary because machine code itself is primitive, and instructions typically have 0-3 operands. Thanks to both this, and Nectar itself being highly low-level, the need for an IR disappears.
|
||||||
|
|
||||||
|
But make no mistake: this approach does not simplify the compiler! At best this moves the complexity to the normalization. Along each step you must make sure the AST is in a valid state. Almost every bug will be silent, and will manifest as misbehavior or malformed results.
|
||||||
|
|
||||||
|
Currently the compiler is designed with only i386+ processors in mind. I intend to add support for i286- and other exotic processors, but I honestly don't see it happening ever, especially if this remains a solo project. RISCier architectures with regular register files will be easier to add support for, but as they're likely designed with C in mind, the advantages of this programming language aren't worth the squeeze.
|
||||||
|
|
||||||
|
## AST structure
|
||||||
|
|
||||||
|
Starting with a Nectar source file, the compiler begins with the common passes: lexing, skimming and parsing. Skimming exploits quirks in Nectar's syntax, and may jump back and forth multiple times to find all symbols and types, as an automatic forward declaration. At the end, parsing returns what is called an AST in the source.
|
||||||
|
|
||||||
|
An AST node may not be shared by multiple parent nodes. Also, the internal Nectar AST does not have scaling for pointer arithmetic; all pointers behave as `u8*`. This is the first of many simplifications.
|
||||||
|
|
||||||
|
Each block of code between `{` and `}` is called a "chunk", a term from Lua. Chunks may contain one another; the highest one within a function is called the top-level chunk (TLC). During parsing of a chunk, definitions of vars/symbols are collected into the relevant `Scope`. After a chunk is parsed, all local variables in the chunk's `Scope` are copied to the TLC's list of local variables, for subsequent analysis.
|
||||||
|
|
||||||
|
However, top-level chunks may contain other top-level chunks, because user-defined functions are within the "global scope", which is considered a function in itself. After all, nothing stops you from directly inserting instructions in the `.text` section of an executable, without attaching it to a label.
|
||||||
|
|
||||||
|
There's enough passes to justify a generic way to invoke the visitor pattern on the AST. Because passes may do many different things to the AST, including modify it, the definition of a generic visitor is very broad. Most functionality is unused by each pass, but all of it is needed.
|
||||||
|
|
||||||
|
void generic_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, void *ud, GenericVisitorHandler preHandler, GenericVisitorHandler postHandler);
|
||||||
|
|
||||||
|
`*nptr` is the actual node that is currently being visited. It is behind an additional indirection, because the node may be replaced by another.
|
||||||
|
|
||||||
|
If the current node is within a statement (most are), `stmt` is equal to that statement. `stmtPrev` is the previous statement. This is necessary in case a pass generates new statements, patching in the linked list of statements. If there is no previous statement, then the head pointer of the list must be patched through the `chu` node. `tlc` is the top-level chunk, which may be equal to `chu`.
|
||||||
|
|
||||||
|
A handler may be called before (pre) or after (post) delving deeper into the tree. Most passes use the prehandler, but type checking will be better with a posthandler, since we want type checks to happen bottom to top.
|
||||||
|
|
||||||
|
## Desegmentation
|
||||||
|
|
||||||
|
Pointers in Nectar might not be regular integers. The basic (near) ones are, but we also have far pointers to support x86 segmentation. Internally these are record types with two fields: a `u16` segment and a `T*` near pointer.
|
||||||
|
|
||||||
|
`ast_segmented_dereference` takes each instance of a far pointer dereference and converts it to a segment load & near pointer dereference. For example, with a `u8 @far* x;`, `y = *x` becomes:
|
||||||
|
|
||||||
|
$segtemp_0 = x.segment;
|
||||||
|
y = *x.offset;
|
||||||
|
|
||||||
|
`$segtemp_0` is colored to `ds`. Other segment registers exist in x86; they're unsupported for now.
|
||||||
|
|
||||||
|
There's also huge pointers, but they're basically far pointers + some QoL.
|
||||||
|
|
||||||
|
## SRoA
|
||||||
|
|
||||||
|
A record variable that needn't be stored in memory should be split into the fields that compose it. This can help produce faster machine code. SRoA is handled by `src/ast/sroa.c`.
|
||||||
|
|
||||||
|
The decision to split a record depends on two factors: the record size and whether the record is ever referenced with `&`. A superfluous `&` is harmful for SRoA and should be removed by prior optimization passes, but this is unimplemented.
|
||||||
|
|
||||||
|
## Pre-normalization
|
||||||
|
|
||||||
|
We make some one-time changes to the AST that normalization itself shouldn't take care of.
|
||||||
|
|
||||||
|
For one thing, arguments are not magically loaded with the correct values. The `pre_dumb_visitor` pass inserts assignment statements at the beginning of the function to load the values from the stack, as per the C ABI.
|
||||||
|
|
||||||
|
Then we have structures which, of course, x86 doesn't support. There must be no by-value use of a structure anywhere. The `decompose_symbol_record_field_access` pass decomposes all references to a structure's field to pointer-like accesses. For example, `a.b` becomes `*((&a + x) as B*)`, where `B` is the type of `a.b`, and `x` is the memory offset of the field `a.b`. The same pattern is specially recognized by the code generator for outputting an x86 address mode like `[eax + x]`.
|
||||||
|
|
||||||
|
Afterward, local structs must be either spilled to global memory or to the stack. This is done by `ast_spill_to_stack`. Since spilling causes the stack frame of the function to grow, all references to the stack must be updated accordingly. This is also done by `spill2stack_visitor`, and in such a way so as to not require more normalization.
|
||||||
|
|
||||||
|
Now a deeper structure access like `a.b.c` (`*((&(*((&a + x) as B*)) + y) as C*)`) is not recognized by the code generator. We must rely on optimizations passes that are guaranteed to happen: `*&p` and `&*p` should become `p`, `p as A* as B*` should become `p as B*` and `(p + x) as A* + y` should become `(p + (x + y)) as A*`. These are done by `ast_denoop`.
|
||||||
|
|
||||||
|
## Normalization
|
||||||
|
|
||||||
|
The idea of turning the AST progressively primitive is called "normalization" in the source. The most simple example would be the following:
|
||||||
|
|
||||||
|
a = -b
|
||||||
|
|
||||||
|
which should become
|
||||||
|
|
||||||
|
a = b
|
||||||
|
a = -a
|
||||||
|
|
||||||
|
Because the `neg` instruction on x86 is single-operand. If targeting an arch like MIPS, this specific normalization would not be used, because one can use the 3-operand `subu` with the zero register.
|
||||||
|
|
||||||
|
Another rule is to extract function arguments and place them into local variables, but *only* if they do not form an x86 operand (for example `5` is ok because `push 5` exists).
|
||||||
|
|
||||||
|
Normalization must be repeated until there are no more changes. The normalization part of the source is responsible for making sure the resulting AST is "trivially compilable" to the machine code. For example, `a = a + b` is trivially compilable, because we have the `add reg, reg` instruction. What is trivially compilable depends on which registers are used in the end (a variable colored as `edi`, `esi` or `ebp` cannot be used for 8-bit stores/loads). These details are not taken into account by normalization.
|
||||||
|
|
||||||
|
Putting all of this together, here is an example of nctref's normalization of the following Fibonacci implementation, as of writing. Here is the main Nectar source code:
|
||||||
|
|
||||||
|
fibonacci: u32(u32 n) -> {
|
||||||
|
if(n <= 1) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
return fibonacci(n - 1) + fibonacci(n - 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
And the processed AST output by the compiler:
|
||||||
|
|
||||||
|
u32(u32) fibonacci: u32(u32) {
|
||||||
|
n = *(@stack + 4);
|
||||||
|
if((n <= 1)) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
$dumb2 = n;
|
||||||
|
$dumb2 = ($dumb2 - 1);
|
||||||
|
$dumb0 = fibonacci($dumb2);
|
||||||
|
$dumb3 = n;
|
||||||
|
$dumb3 = ($dumb3 - 2);
|
||||||
|
$dumb1 = fibonacci($dumb3);
|
||||||
|
$dumb0 = ($dumb0 + $dumb1);
|
||||||
|
return $dumb0;
|
||||||
|
};
|
||||||
|
|
||||||
|
`@stack` is an internal variable that points to the beginning of the current stack frame.
|
||||||
|
|
||||||
|
## Use-def chain
|
||||||
|
|
||||||
|
***WARNING: THIS ENTIRE SECTION HAS BECOME OUTDATED***
|
||||||
|
|
||||||
|
I hate these things. Another is def-use chains, but both are horribly underdocumented. Their only use in most literature is so the author can immediately move to SSA form.
|
||||||
|
|
||||||
|
For each variable, its UD chain is a list of each usage in the AST, with the corresponding potential definition of the variable at that use. For each potential definition that exists at that point, there is one UD element in the chain. If there's only one potential definition at a point, then it's definitely the true one. Users of UD chains include optimizers and codegen. UD chains are always regenerated for use between passes by using the UD visitor on the top-level chunk.
|
||||||
|
|
||||||
|
As simplest, the code `u8 x = 0;` has an empty UD-chain, because there are no uses. It's definition could even be classified as dead code.
|
||||||
|
|
||||||
|
Clearly, a definition of a variable overrides every definition before it, but that is only within a basic block. In the following code, a variable has a single potential definition in each branch of the if statement, but afterward it will have two:
|
||||||
|
|
||||||
|
u8 x = 0; /* Potential definitions: [x = 0]
|
||||||
|
* UD-chain of x:
|
||||||
|
* empty */
|
||||||
|
if(y) {
|
||||||
|
x = 1;
|
||||||
|
f1(x); /* Potential definitions: [x = 1]
|
||||||
|
* UD-chain of x:
|
||||||
|
* - def=[x = 1], use=[f1(x)] */
|
||||||
|
} else {
|
||||||
|
x = 2;
|
||||||
|
f2(x); /* Potential definitions: [x = 2]
|
||||||
|
* UD-chain of x:
|
||||||
|
* - def=[x = 1], use=[f1(x)]
|
||||||
|
* - def=[x = 2], use=[f2(x)] */
|
||||||
|
}
|
||||||
|
f3(x); /* Potential definitions: [x = 1], [x = 2]
|
||||||
|
* UD-chain of x:
|
||||||
|
* - def=[x = 1], use=[f1(x)]
|
||||||
|
* - def=[x = 2], use=[f2(x)]
|
||||||
|
* - def=[x = 1], use=[f3(x)]
|
||||||
|
* - def=[x = 2], use=[f3(x)] */
|
||||||
|
|
||||||
|
It gets worse. Imagine the following pseudocode:
|
||||||
|
|
||||||
|
x = 0;
|
||||||
|
loop {
|
||||||
|
x = x + 1;
|
||||||
|
y = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
The UD-chain knows nothing about loops. It only cares whether something comes before or after. As is, it'll assume y is not in conflict with x, and they'll end up in the same register. Because of this, the parser must insert a so-called "loop guard", which will turn the AST into the following:
|
||||||
|
|
||||||
|
x = 0;
|
||||||
|
loop {
|
||||||
|
x = x + 1;
|
||||||
|
y = 5;
|
||||||
|
}
|
||||||
|
x;
|
||||||
|
|
||||||
|
That's one problem, but there's another:
|
||||||
|
|
||||||
|
x = 0;
|
||||||
|
loop {
|
||||||
|
f(x);
|
||||||
|
x = x + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Despite appearing later in the source, `x = x + 1` is a potential definition for `f(x)`! This means the UD-chain generator must go through loops twice -- once with the upper definitions, and once with definitions from within the loop. Additionally, the UD-chain is assumed to be ordered by appearence in the source, so insertion in the second pass must consider that.
|
||||||
|
|
||||||
|
Now, why did I choose UD chains? Why, simplicity, obviously...
|
||||||
|
|
||||||
|
## Coloring
|
||||||
|
|
||||||
|
At this point we have a very distorted kind of Nectar AST in our function. Sure we've got blocks and other familiar things, but all variables are in a flat list. These variables are essentially the "virtual registers" you hear a lot about. Because x86 only has six general-purpose registers, we must assign each of these variables (VTEs) to a physical machine register.
|
||||||
|
|
||||||
|
The x86 register set is highly irregular:
|
||||||
|
|
||||||
|
1. `ax` may be split into two halves (`ah` and `al`), but `eax` may not be split. Likewise with `ebx`, `ecx` and `edx`, but nothing else.
|
||||||
|
2. The low bytes of `si`, `di`, `bp` and `sp` are accessible only in 64-bit mode.
|
||||||
|
3. Registers `r8` through `r15` are accessible only in 64-bit mode.
|
||||||
|
4. If an instruction uses any 64-bit-only register, then `ah`, `bh`, `ch`, `dh` are not accessible.
|
||||||
|
5. Of the 16-bit registers, only `bx`, `bp`, `di` and `si` may be dereferenced. Any 32-bit register can be dereferenced, including in 16-bit mode.
|
||||||
|
6. Obviously, floating-point registers, control registers, debug registers form separate spaces.
|
||||||
|
|
||||||
|
It is insufficient for a compiler to assume these are in any way comparable. The trick is to imagine the CPU as a bitmap of resources.
|
||||||
|
|
||||||
|
`al` and `ah` are separate registers on their own, so we assign 1 bit to each (for clarity, let it be ints 1 and 2). `ax` is also a separate register, but as it is the concatenation of `al` and `ah`, it is assigned the OR of both bits (int 3). You could reserve a bit for the high 16 bits of `eax`, but as it is inaccessible, you may as well give the same bits of `ax` to `eax`. Repeat the same for the rest of the registers.
|
||||||
|
|
||||||
|
From these we form sets of registers called "register classes", which can be thought of as "ways in which a register can be used". The resource mask of a register class is the union (bitwise OR) of all bits used by all of its registers.
|
||||||
|
|
||||||
|
This compiler currently considers 3 register classes: `REG_CLASS_8`, for `al`, `ah`, `bl`, `bh`, `cl`, `ch`, `dl`, `dh`; `REG_CLASS_NOT_8` for `ax`, `eax`, `bx`, `ebx`, `cx`, `ecx`, `dx`, `edx`, `di`, `edi`, `si`, `esi`; `REG_CLASS_IA16_PTRS` for `di`, `si`, `bx`. It can be seen registers are not unique under this abstraction, but this is necessary as we gave up finding any patterns and assume the CPU to be a soup.
|
||||||
|
|
||||||
|
(64-bit is not considered)
|
||||||
|
|
||||||
|
Finally we begin coloring, a large area of study in itself, but a common approach is to imagine it as a graph coloring problem, where vertices are VTEs, and edges connect conflicting VTEs that cannot have the same color. "Color" in this case means the register class and index within the register class. Edges in the graph are determined using the UD-chains of both VTEs and their resource masks (registers with an empty resource mask intersection cannot interfere).
|
||||||
|
|
||||||
|
The actual coloring algorithm used is Welsh-Powell, which sorts the VTEs/vertices by degree before attempting greedy coloring.
|
||||||
|
|
||||||
|
If there's more colors than there are physical registers, then we have a conflict, and must spill. `spill2stack` transforms every use of a local variable (`ASTExprVar` where its VTE is of type `SCOPEITEM_VAR`) into the form `@stack + n`.
|
||||||
|
|
||||||
|
If spill2stack is used, then CG must fail once so that normalization can be applied again.
|
||||||
|
|
||||||
|
## Pre-coloring
|
||||||
|
|
||||||
|
NOTE: `spill2var` turned out to be pushing the problem a step back rather than solving it. Because it is known in advance what must be pre-colored, any such expressions are immediately placed in their own variable by another pass. If the assignment turns out to have been redundant, the register allocator should coaslesce the moves.
|
||||||
|
|
||||||
|
Pre-coloring is a necessary evil. It is *very* common in x86 that an instruction's operand is some fixed register we cannot change. But even worse: pre-coloring cannot be handled by
|
||||||
|
|
||||||
|
## Callee-saved pass
|
||||||
|
|
||||||
|
If a function uses a callee-saved register, these must be stored and loaded at the correct times. This is done by `callee_saved` in `src/x86/cg.c`. If the hardware resources used by any variable overlaps with those of a callee-saved register, said register is marked. Afterward, these registers are passed to the code generator.
|
||||||
|
|
||||||
|
## Code generation
|
||||||
|
|
||||||
|
FINALLY. This pass doesn't use `generic_visitor`, because it may consume multiple sibling AST nodes for emitting code. At this point there's nothing arcane or obscure; the code is pretty self-explanatory.
|
||||||
|
|
||||||
|
Using the same Fibonacci example as above, this is the result.
|
||||||
|
|
||||||
|
global fibonacci
|
||||||
|
fibonacci:
|
||||||
|
mov edx, [esp + 4]
|
||||||
|
cmp edx, 1
|
||||||
|
ja .L0
|
||||||
|
mov eax, edx
|
||||||
|
ret
|
||||||
|
.L0:
|
||||||
|
mov eax, edx
|
||||||
|
dec eax
|
||||||
|
push ecx
|
||||||
|
push edx
|
||||||
|
push eax
|
||||||
|
call fibonacci
|
||||||
|
add esp, 4
|
||||||
|
pop edx
|
||||||
|
pop ecx
|
||||||
|
mov ecx, eax
|
||||||
|
mov eax, edx
|
||||||
|
sub eax, 2
|
||||||
|
push ecx
|
||||||
|
push edx
|
||||||
|
push eax
|
||||||
|
call fibonacci
|
||||||
|
add esp, 4
|
||||||
|
pop edx
|
||||||
|
pop ecx
|
||||||
|
add ecx, eax
|
||||||
|
mov eax, ecx
|
||||||
|
ret
|
||||||
|
|
||||||
|
## Generics
|
||||||
|
|
||||||
|
**NOTE: I intend to place this section in a different Markdown file entirely. It will be simply too big.**
|
||||||
|
|
||||||
|
record Foo[T, U, V] {
|
||||||
|
T t;
|
||||||
|
U u;
|
||||||
|
V v;
|
||||||
|
}
|
||||||
|
|
||||||
|
Nectar does generics similarly to C++. Structures are simple to make generic. When parsing a generic structure definition we must introduce a new scope, so we can introduce the generic types as instances of `TypeGeneric`. If we encounter a parametrization like `Foo[u8, u16, u32]`, we walk up the tree formed by the type of `Foo`, and replace all `TypeGeneric` instances with the concrete types. This is done by `type_parametrize` which takes a `Parametrization` structure. Note that generic type names are not used, but the indices at which they appear.
|
||||||
|
|
||||||
|
bar: [T]T(T a, T b) -> {
|
||||||
|
return a + b;
|
||||||
|
};
|
||||||
|
|
||||||
|
If a function is defined with a generic type, parsing it is skipped until an explicit instantiation. This is because type checking is coupled with parsing. It needn't be this way, but it's a refactoring I'm not interested in doing at the moment. This ended up bringing other complexities. Because of the parser-type checker coupling, we must know what a generic type's name originally was, so `TypeGeneric`s must store this in addition to the index.
|
||||||
|
|
||||||
|
@instantiate bar[u32];
|
||||||
|
|
||||||
|
Upon parsing the above statement, the parser:
|
||||||
|
|
||||||
|
1. Creates a new scope
|
||||||
|
2. Finds the generic type names (using an output value of `type_parametrize` not mentioned until now)
|
||||||
|
3. Inserts the concrete types into the scope under the generic type names
|
||||||
|
4. Jumps to the generic function definition (in fact, to *right after the `[...]` block* to ignore the genericness)
|
||||||
|
5. Begins parsing the function's code block
|
||||||
|
6. Pops the scope
|
||||||
|
7. Jumps back to the end of the `@instantiate` statement
|
||||||
|
8. Insert the function code block into a new symbol, appending the concrete type names to the original function name separated by underscores (`bar_u32`)
|
||||||
|
|
||||||
|
How's that for a hack?
|
||||||
|
|
||||||
|
## x86 Segmentation
|
||||||
|
|
||||||
|
In x86, absolutely all memory access by software goes through one of six "segments", which translates the address from the segment's space to true linear space.
|
||||||
|
|
||||||
|
In 32-bit and above, segmentation has mostly been forgotten. Major OSes set all\* segments to the same setting, letting software pretend that segmentation doesn't exist. This is called a flat memory model. In 16-bit, pointers are enough only for 64kB of memory, so Intel added segmentation to expand the memory space to 20-bit, giving the 8086 one megabyte to use.
|
||||||
|
|
||||||
|
This splits the pointer type into three subtypes:
|
||||||
|
|
||||||
|
1. Near: the kind of pointer everyone knows, which uses some "default" segment
|
||||||
|
2. Far: a pair of integers, one which defines the segment, the other defines the memory offset
|
||||||
|
3. Huge: a far pointer that is slower but more well-behaved
|
||||||
|
|
||||||
|
By default, pointers in Nectar are just integers, i.e. near, limiting a Nectar program to only 64kB. The parameter `mem` sets all unspecified pointers in a Nectar source file to huge pointers.
|
||||||
|
|
||||||
|
Far and huge pointers have to be normalized. Let us have the following:
|
||||||
|
|
||||||
|
u8 @far* a;
|
||||||
|
|
||||||
|
*a = 5;
|
||||||
|
|
||||||
|
a = a + 1;
|
||||||
|
|
||||||
|
A data access goes through the data segment `ds`, so prior to dereferencing, we must make sure `ds` is set to the segment part of `a`.
|
||||||
|
|
||||||
|
FarPointer a;
|
||||||
|
|
||||||
|
$sreg_ds = a.segment;
|
||||||
|
*a.offset = 5;
|
||||||
|
|
||||||
|
a.offset = a.offset + 1;
|
||||||
|
|
||||||
|
This example benefits from scalar replacement:
|
||||||
|
|
||||||
|
u16 a_segment;
|
||||||
|
u16 a_offset;
|
||||||
|
|
||||||
|
$sreg_ds = a_segment;
|
||||||
|
*a_offset = 5;
|
||||||
|
|
||||||
|
a_offset = a_offset + 1;
|
||||||
|
|
||||||
|
## Other problems with this approach (1)
|
||||||
|
|
||||||
|
In the past, a function in the AST was essentially a tree of blocks, following the "structured programming" of Nectar source code. This was a poor choice for multiple reasons.
|
||||||
|
|
||||||
|
Short-circuit evaluation is when the evaluation of an expression is guaranteed to stop once the output is already known. For example, if in `A || B` `A` is already truthy, then `B` is not evaluated. This is not an optimization, but an important semantical detail.
|
||||||
|
|
||||||
|
Let us write `if(x == 1 || y == 1) { do stuff; }` in x86:
|
||||||
|
|
||||||
|
cmp eax, 1
|
||||||
|
je .L1
|
||||||
|
cmp ebx, 1
|
||||||
|
jne .L2
|
||||||
|
.L1:
|
||||||
|
; do stuff
|
||||||
|
.L2:
|
||||||
|
|
||||||
|
Note that the two jump instructions are basically goto statements. With a tree of blocks, this kind of logic isn't feasible.
|
||||||
|
|
||||||
|
Even worse, normalization will try to move the condition into a variable and normalize it further, creating the following:
|
||||||
|
|
||||||
|
u1 z = x == 1;
|
||||||
|
u1 w = y == 1;
|
||||||
|
u1 k = z || w;
|
||||||
|
if(k) {
|
||||||
|
do stuff;
|
||||||
|
}
|
||||||
|
|
||||||
|
And now we need 2 new registers for no reason..
|
||||||
|
|
||||||
|
What else? Did you know `return` statements are gotos when a function is inlined, too? In other words, no function inlining.
|
||||||
|
|
||||||
|
## Adding a Feature
|
||||||
|
|
||||||
|
When adding a feature, first write it out in Nectar in the ideal normalized form. Make sure this compiles correctly. Afterward, implement normalization rules so that code can be written in any fashion. If specific colorings are required, then the pre-coloring and spill2var passes must be updated. The following is an example with multiplication, as this is what I'm adding as of writing.
|
||||||
|
|
||||||
|
Note the way `mul` works on x86 (yes, I'm aware `imul` exists). Firstly, one of the operands is the destination, because `mul` is a 2-op instruction. Secondly, the other operand cannot be an immediate, because it is defined as r/m (register or memory), so if the second operand is a constant, it must be split into a variable (`varify` in `dumberdowner.c`). Thirdly, the destination must be the A register, so one of the operands must be pre-colored to A. Fourthly, `mul` clobbers the D register with the high half of the product. In other words, we have an instruction with *two* output registers, which the Nectar AST does not support. But we can't have the register allocator assign anything to D here.
|
||||||
|
|
||||||
|
To account for this, we can have a second assignment statement right next to the multiplication. Because the main multiplication clobbers the source operand, the mulhi assignment must come before the mul. Putting all this together, this is the canonical way to do `z = x * y` with an x86 target:
|
||||||
|
|
||||||
|
z = x;
|
||||||
|
w = z *^ y;
|
||||||
|
z = z * y;
|
||||||
|
|
||||||
|
But this is without pre-coloring. We want precolored nodes to live as little as possible, because separately solving pre-coloring collisions whilst also keeping the code normalized *and* not horrible turned out to be practically impossible (spill2var).
|
||||||
|
|
||||||
|
k = x;
|
||||||
|
w = k *^ y;
|
||||||
|
k = k * y;
|
||||||
|
z = k;
|
||||||
|
|
||||||
|
Where `k` and `w` are the VTEs that must live and die immediately. The normalizer checks if `w` and `k` are already precolored to not loop forever, but it would be better to check the UD-chain.
|
||||||
|
|
||||||
|
Lastly, the codegen pass must recognize the above sequence as a multiplication and emit a single `mul` instruction.
|
||||||
|
|
||||||
|
In `cg.c` is a function called `xop`, which returns an x86 operand string, given a trivially compilable Nectar expression. Because we've guaranteed the other operand may not be a constant, we do not need to check the XOP type, but it's a good idea to insert `assert`s and `abort`s everywhere to prevent hard-to-find bugs.
|
||||||
|
|
||||||
|
Once all that is done and tested, now we can add the following normalization rules: all binary operations with the operand `AST_BINOP_MUL` or `AST_BINOP_MULHI` must be the whole expression within an assignment statement. If not, extract into a separate assignment & new variable with `varify`. The destination of the assignment, and both operands of the binary operation must be of type `AST_EXPR_VAR`, with their corresponding variables being of type `SCOPEITEM_VAR`, not `SCOPEITEM_SYMBOL`, `SCOPEITEM_TYPE` nor `SCOPEITEM_CEXPR`. If any of those don't apply, `varify` the offenders. Each such assignment have a neighboring, symmetric assignment, so that both A and D are caught by the pre-coloring pass.
|
||||||
|
|
||||||
|
A common bug when writing a normalization rule is ending up with one that is always successful. If this happens, the compiler will become stuck endlessly normalizing, which is nonsense. It would be nice if you could formally prove that won't happen. Another common bug is not realizing the order in which normalization rules are applied matters :).
|
||||||
|
|
||||||
|
You know, I really regret writing this in C.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Oh God.. and for what? So it runs on MS-DOS?? Was it worth it? It doesn't even work there; it crashes!
|
||||||
23
Makefile
23
Makefile
@@ -3,19 +3,26 @@ rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
|
|||||||
SOURCES := $(call rwildcard,src/,*.c)
|
SOURCES := $(call rwildcard,src/,*.c)
|
||||||
HEADERS := $(call rwildcard,src/,*.h)
|
HEADERS := $(call rwildcard,src/,*.h)
|
||||||
|
|
||||||
|
OBJECTS := $(patsubst src/%.c,build/%.o,$(SOURCES))
|
||||||
|
DEPS := $(patsubst build/%.o,build/%.d,$(OBJECTS))
|
||||||
|
|
||||||
PREFIX = /usr/local
|
PREFIX = /usr/local
|
||||||
|
|
||||||
|
CFLAGS = $(if $(DEBUGA),-DDEBUG=1,) -Wall $(if $(ASAN),-fsanitize=address,) -fno-PIE -no-pie -std=gnu11 $(if $(DEBUG),-O0 -g,-Os -s) -fms-extensions -Isrc -Wno-array-bounds -MMD -MP -flto -Wno-format-zero-length
|
||||||
|
|
||||||
.PHONY: install clean
|
.PHONY: install clean
|
||||||
|
|
||||||
ntc: $(SOURCES) $(HEADERS)
|
build/%.o: src/%.c
|
||||||
ifdef OW
|
mkdir -p $(@D)
|
||||||
wcl $(if $(GAS),-DSYNTAX_GAS=1,) $(if $(DEBUG),-DDEBUG=1,) -fe="ntc.exe" -0 -bcl=dos -mt $(if $(DEBUG),,-d0 -os -om -ob -oi -ol -ox) -lr -za99 -i=src $(SOURCES)
|
$(CC) -c $(CFLAGS) -o $@ $<
|
||||||
else
|
|
||||||
cc $(if $(GAS),-DSYNTAX_GAS=1,) $(if $(DEBUG),-DDEBUG=1,) -Wall -o ntc -fno-PIE -no-pie -std=gnu11 $(if $(DEBUG),-O0 -g,-Os -s) -fms-extensions -Isrc $(SOURCES)
|
ntc: $(OBJECTS)
|
||||||
endif
|
$(CC) $(CFLAGS) -o ntc $(OBJECTS)
|
||||||
|
|
||||||
install: ntc
|
install: ntc
|
||||||
mv ./ntc $(PREFIX)/bin
|
cp ./ntc $(PREFIX)/bin
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm ./ntc
|
rm ./ntc $(OBJECTS)
|
||||||
|
|
||||||
|
-include $(DEPS)
|
||||||
|
|||||||
27
README.md
27
README.md
@@ -1,11 +1,19 @@
|
|||||||
# N19 Reference Compiler
|
# N19 Reference Compiler
|
||||||
|
|
||||||
Made to compile fast and produce not great, but acceptable output. Currently only 386 output supported (protected and partially real mode).
|
Made to compile fast and produce acceptable output. Currently only 386 output supported (protected and partially real mode).
|
||||||
|
|
||||||
|
Composed of the following passes:
|
||||||
|
|
||||||
|
1. Lexing
|
||||||
|
2. Parsing & loop\_second\_pass
|
||||||
|
3. Dumbification
|
||||||
|
4. Codegen
|
||||||
|
|
||||||
|
UD-chains are generated during parsing. Codegen uses on primitive patterns within the AST, possible thanks to dumbification. This technique is applicable owing to Nectar's already low-level nature
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
make
|
make
|
||||||
|
|
||||||
sudo make install
|
sudo make install
|
||||||
|
|
||||||
# Command-line usage
|
# Command-line usage
|
||||||
@@ -19,10 +27,23 @@ This will be ported into a man page later:
|
|||||||
(TBA) x86_target: Target processor feature set (0 for 8086, 3 for 80386, m for generic x86_64)
|
(TBA) x86_target: Target processor feature set (0 for 8086, 3 for 80386, m for generic x86_64)
|
||||||
(TBA) x86_mode: Target operating mode (16 for 16-bit real mode, 32 for 32-bit protected mode or long mode, 64 for 64-bit long mode)
|
(TBA) x86_mode: Target operating mode (16 for 16-bit real mode, 32 for 32-bit protected mode or long mode, 64 for 64-bit long mode)
|
||||||
in: Input Nectar source file
|
in: Input Nectar source file
|
||||||
|
inc: Additional module include path
|
||||||
|
|
||||||
Unknown arguments are ignored.
|
Unknown arguments are ignored.
|
||||||
|
|
||||||
# Example
|
# Example 1
|
||||||
|
|
||||||
|
$ ntc < tests/parsenum.nct > out.asm
|
||||||
|
$ nasm -f elf -o out.o out.asm
|
||||||
|
$ ld -dynamic-linker /lib/ld-linux.so.2 -m elf_i386 -o parsenum out.o -lc
|
||||||
|
$ printf "123" | ./parsenum
|
||||||
|
$ echo $?
|
||||||
|
123
|
||||||
|
$ printf "05" | ./parsenum
|
||||||
|
$ echo $?
|
||||||
|
5
|
||||||
|
|
||||||
|
# Example 2
|
||||||
|
|
||||||
$ ntc < tests/bf.nct > out.asm
|
$ ntc < tests/bf.nct > out.asm
|
||||||
$ nasm -f elf -o out.o out.asm
|
$ nasm -f elf -o out.o out.asm
|
||||||
|
|||||||
472
documentation_passes.svg
Normal file
472
documentation_passes.svg
Normal file
@@ -0,0 +1,472 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="64.350662mm"
|
||||||
|
height="191.12521mm"
|
||||||
|
viewBox="0 0 64.350662 191.12521"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
|
||||||
|
sodipodi:docname="documentation_passes.svg">
|
||||||
|
<metadata
|
||||||
|
id="metadata57">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.3968848"
|
||||||
|
inkscape:cx="132.64522"
|
||||||
|
inkscape:cy="370.56552"
|
||||||
|
inkscape:window-width="3840"
|
||||||
|
inkscape:window-height="2109"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
fit-margin-top="1"
|
||||||
|
fit-margin-bottom="1"
|
||||||
|
fit-margin-left="1"
|
||||||
|
fit-margin-right="1" />
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<marker
|
||||||
|
style="overflow:visible"
|
||||||
|
id="Arrow1Lend"
|
||||||
|
refX="0"
|
||||||
|
refY="0"
|
||||||
|
orient="auto"
|
||||||
|
inkscape:stockid="Arrow1Lend"
|
||||||
|
inkscape:isstock="true">
|
||||||
|
<path
|
||||||
|
transform="matrix(-0.8,0,0,-0.8,-10,0)"
|
||||||
|
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
|
||||||
|
d="M 0,0 5,-5 -12.5,0 5,5 Z"
|
||||||
|
id="path940" />
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(28.296833,1.0320015)">
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="0.13249999" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="-0.018652661"
|
||||||
|
y="6.828989"
|
||||||
|
id="text5232"><tspan
|
||||||
|
id="tspan104126"><tspan
|
||||||
|
style="text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104124">Lexing</tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="15.1325" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="0.0091233542"
|
||||||
|
y="21.80534"
|
||||||
|
id="text5232-1"><tspan
|
||||||
|
id="tspan104130"><tspan
|
||||||
|
style="text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104128">Skimming</tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3-7"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="30.1325" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="-0.13216139"
|
||||||
|
y="36.702583"
|
||||||
|
id="text5232-1-5"><tspan
|
||||||
|
id="tspan104134"><tspan
|
||||||
|
style="text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104132">Parsing</tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3-7-9"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="45.1325" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="0.23284389"
|
||||||
|
y="51.497505"
|
||||||
|
id="text5232-1-5-6"><tspan
|
||||||
|
id="tspan104138"><tspan
|
||||||
|
style="text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104136">Desegmentation</tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3-7-9-7"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="60.1325" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="0.14818139"
|
||||||
|
y="66.713989"
|
||||||
|
id="text5232-1-5-6-5"><tspan
|
||||||
|
id="tspan104142"><tspan
|
||||||
|
style="text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104140">SRoA</tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3-7-9-7-6"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="135.13251" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="0.083988138"
|
||||||
|
y="141.51147"
|
||||||
|
id="text5232-1-5-6-5-4"><tspan
|
||||||
|
id="tspan104146"><tspan
|
||||||
|
style="text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104144">Denooping</tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3-7-9-7-6-38"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="165.13252" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="0.033953242"
|
||||||
|
y="171.85045"
|
||||||
|
id="text5232-1-5-6-5-4-6"><tspan
|
||||||
|
id="tspan104150"><tspan
|
||||||
|
style="text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104148">Dumbification</tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3-7-9-7-6-38-0"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="180.13252" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="0.25064546"
|
||||||
|
y="186.79578"
|
||||||
|
id="text5232-1-5-6-5-4-6-4"><tspan
|
||||||
|
id="tspan104154"><tspan
|
||||||
|
style="text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104152">Try Codegen</tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3-7-9-7-6-3"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="150.13252" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="-0.19107197"
|
||||||
|
y="156.62112"
|
||||||
|
id="text5232-1-5-6-5-4-3"><tspan
|
||||||
|
id="tspan104158"><tspan
|
||||||
|
style="text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104156">Commutativity</tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3-7-9-7-1"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="120.1325" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="0.017620174"
|
||||||
|
y="126.4618"
|
||||||
|
id="text5232-1-5-6-5-89"><tspan
|
||||||
|
id="tspan104162"><tspan
|
||||||
|
style="text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104160">Record Spilling</tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3-7-9-7-3"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="90.1325" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="-0.016195312"
|
||||||
|
y="96.573265"
|
||||||
|
id="text5232-1-5-6-5-8"><tspan
|
||||||
|
id="tspan104166"><tspan
|
||||||
|
style="text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104164">Predumbing</tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3-7-9-7-3-8"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="105.1325" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:0.25em;font-family:sans-serif;white-space:pre;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="-0.052187458"
|
||||||
|
y="109.01695"
|
||||||
|
id="text101633"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan2359"
|
||||||
|
x="-0.052186634"
|
||||||
|
y="109.01695"><tspan
|
||||||
|
id="tspan104172"><tspan
|
||||||
|
style="font-size:4.23333px;-inkscape-font-specification:'sans-serif, Normal';text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104168">Decompose Record</tspan><tspan
|
||||||
|
dx="2.6871719"
|
||||||
|
style="font-size:4.23333px;-inkscape-font-specification:'sans-serif, Normal';text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104170" /></tspan><tspan
|
||||||
|
id="tspan104176"><tspan
|
||||||
|
style="font-size:4.23333px;-inkscape-font-specification:'sans-serif, Normal';text-align:center;text-anchor:middle"
|
||||||
|
id="tspan104174"></tspan></tspan></tspan><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan2361"
|
||||||
|
x="-0.052187435"
|
||||||
|
y="113.31228"><tspan
|
||||||
|
id="tspan2363"><tspan
|
||||||
|
style="font-size:4.23333px;-inkscape-font-specification:'sans-serif, Normal';text-align:center;text-anchor:middle"
|
||||||
|
id="tspan2365">Field Access</tspan></tspan></tspan></text>
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.329003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="rect31-3-7-9-7-65"
|
||||||
|
width="54.232807"
|
||||||
|
height="8.7961817"
|
||||||
|
x="-27.132332"
|
||||||
|
y="75.1325" />
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||||
|
x="-19.417631"
|
||||||
|
y="81.833992"
|
||||||
|
id="text933"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan931"
|
||||||
|
x="-19.417631"
|
||||||
|
y="81.833992"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.99722px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.264583">Linearization</tspan></text>
|
||||||
|
<g
|
||||||
|
id="g1321">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1321-4"
|
||||||
|
transform="translate(0,15.000143)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315-8" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317-1" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1321-4-0"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,-1.5064804e-4,30.000115)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315-8-3" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317-1-0" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1321-4-0-4"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,-1.5064804e-4,45.000116)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315-8-3-4" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317-1-0-4" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1321-4-0-4-4"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,-1.5064804e-4,60.000116)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315-8-3-4-7" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317-1-0-4-6" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1321-4-0-4-4-3"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,-1.5064804e-4,75.000116)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315-8-3-4-7-1" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317-1-0-4-6-7" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1321-4-0-4-4-3-5"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,-1.5064804e-4,90.000116)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315-8-3-4-7-1-9" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317-1-0-4-6-7-6" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1321-4-0-4-4-3-5-2"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,-1.5064804e-4,105.00012)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315-8-3-4-7-1-9-1" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317-1-0-4-6-7-6-7" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1321-4-0-4-4-3-5-2-8"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,-1.5064804e-4,120.00012)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315-8-3-4-7-1-9-1-5" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317-1-0-4-6-7-6-7-7" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1321-4-0-4-4-3-5-2-8-4"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,-1.5064804e-4,135.00012)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315-8-3-4-7-1-9-1-5-1" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317-1-0-4-6-7-6-7-7-8" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1321-4-0-4-4-3-5-2-8-4-5"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,-1.5064804e-4,150.00012)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315-8-3-4-7-1-9-1-5-1-9" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317-1-0-4-6-7-6-7-7-8-7" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1321-4-0-4-4-3-5-2-8-4-5-5"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,-1.5064804e-4,165.00012)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 0.0021762,8.9798566 V 15.048811"
|
||||||
|
id="path1315-8-3-4-7-1-9-1-5-1-9-3" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -0.73956258,14.035325 H 0.74417303 L 0.0021762,15.048811 Z"
|
||||||
|
id="path1317-1-0-4-6-7-6-7-7-8-7-8" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1689"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,34.755416,165.00012)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m -6.5075767,4.3822683 4.0709535,2.3501724 2.58903688,5.3435213 -2.33068508,5.160166 -5.3537243,2.596475"
|
||||||
|
id="path1315-8-3-4-7-1-9-1-5-1-9-3-9"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -6.5833718,3.6560712 V 5.1395446 L -7.5970369,4.3976789 Z"
|
||||||
|
id="path1317-1-0-4-6-7-6-7-7-8-7-8-6" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g1689-3"
|
||||||
|
transform="matrix(0.99982636,0,0,1.0000031,34.755416,15.084195)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m -6.5075767,4.3822683 4.0709535,2.3501724 2.58903688,5.3435213 -2.33068508,5.160166 -5.3537243,2.596475"
|
||||||
|
id="path1315-8-3-4-7-1-9-1-5-1-9-3-9-8"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
<path
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M -6.5833718,3.6560712 V 5.1395446 L -7.5970369,4.3976789 Z"
|
||||||
|
id="path1317-1-0-4-6-7-6-7-7-8-7-8-6-6" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 24 KiB |
4
examples/Alloc.nct
Normal file
4
examples/Alloc.nct
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
record Alloc {
|
||||||
|
u8* userdata;
|
||||||
|
u8*(u8* userdata, u8* ptr, u32 sz)* realloc;
|
||||||
|
}
|
||||||
5
examples/Exporter.nct
Normal file
5
examples/Exporter.nct
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
u32 SYMBOL: 1234;
|
||||||
|
|
||||||
|
record Foo {
|
||||||
|
u32 gaga;
|
||||||
|
}
|
||||||
8
examples/FarPointer.nct
Normal file
8
examples/FarPointer.nct
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
u8 @far* x;
|
||||||
|
u8 @far* y;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
u8 v = *x;
|
||||||
|
*y = v;
|
||||||
|
x.offset = x.offset + 1;
|
||||||
|
}
|
||||||
68
examples/HTTPFileServer.nct
Normal file
68
examples/HTTPFileServer.nct
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
record LibCSockAddrInet6 {
|
||||||
|
u16 family;
|
||||||
|
u16 port;
|
||||||
|
u32 flowinfo;
|
||||||
|
u8[16] address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@section(".text");
|
||||||
|
|
||||||
|
extern u32(u32 domain, u32 type, u32 protocol) socket;
|
||||||
|
extern u32(u32 sockfd, u8* addr, u32 socklen) bind;
|
||||||
|
extern u32(u32 sockfd, u32 backlog) listen;
|
||||||
|
extern u32(u32 sockfd, u8* addr, u32* socklen) accept;
|
||||||
|
extern u32(u32 sockfd, u8* data, u32 length) write;
|
||||||
|
extern u32(u32 sockfd, u8* data, u32 length) read;
|
||||||
|
extern u32(u32 fd) close;
|
||||||
|
|
||||||
|
main: u0() -> {
|
||||||
|
SERV_SOCKET = socket(10, 1, 0);
|
||||||
|
|
||||||
|
LibCSockAddrInet6 addr;
|
||||||
|
addr.family = 10; /* AF_INET6 */
|
||||||
|
addr.port = 43105; /* 25000 in reversed endian */
|
||||||
|
addr.flowinfo = 0;
|
||||||
|
addr.address[0] = 0;
|
||||||
|
addr.address[1] = 0;
|
||||||
|
addr.address[2] = 0;
|
||||||
|
addr.address[3] = 0;
|
||||||
|
addr.address[4] = 0;
|
||||||
|
addr.address[5] = 0;
|
||||||
|
addr.address[6] = 0;
|
||||||
|
addr.address[7] = 0;
|
||||||
|
addr.address[8] = 0;
|
||||||
|
addr.address[9] = 0;
|
||||||
|
addr.address[10] = 0;
|
||||||
|
addr.address[11] = 0;
|
||||||
|
addr.address[12] = 0;
|
||||||
|
addr.address[13] = 0;
|
||||||
|
addr.address[14] = 0;
|
||||||
|
addr.address[15] = 0;
|
||||||
|
bind(SERV_SOCKET, &addr, @sizeof(addr));
|
||||||
|
|
||||||
|
listen(SERV_SOCKET, 16);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
u32 fd = accept(SERV_SOCKET, &addr, &SOCK_LEN);
|
||||||
|
if(fd == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
read(fd, &BUFFER, @sizeof(BUFFER));
|
||||||
|
write(fd, &RESPONSE, @sizeof(RESPONSE));
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
@section(".data");
|
||||||
|
|
||||||
|
u32 SOCK_LEN:;
|
||||||
|
|
||||||
|
u32 SERV_SOCKET:;
|
||||||
|
|
||||||
|
u8[?] RESPONSE: "HTTP/1.0 200 OK\r\nServer: NectarTestHTTPFileServer\r\nContent-Type: text/plain\r\n\r\nYo.";
|
||||||
|
|
||||||
|
u8[1024] BUFFER:;
|
||||||
4
examples/Importer.nct
Normal file
4
examples/Importer.nct
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
use Exporter;
|
||||||
|
|
||||||
|
Foo f;
|
||||||
|
f.gaga = SYMBOL;
|
||||||
14
examples/IrregularAllocation.nct
Normal file
14
examples/IrregularAllocation.nct
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
u8 a = 12;
|
||||||
|
u8 b = 19;
|
||||||
|
u8 c = a + b;
|
||||||
|
u32 d = 1;
|
||||||
|
u8 e = d + 1;
|
||||||
|
u32 f = 10;
|
||||||
|
u32 g = 55;
|
||||||
|
a;
|
||||||
|
b;
|
||||||
|
c;
|
||||||
|
d;
|
||||||
|
e;
|
||||||
|
f;
|
||||||
|
g;
|
||||||
43
examples/ListDLC.nct
Normal file
43
examples/ListDLC.nct
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* ListDLC: Dynamic, Linear growth, C-managed
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern u8*(u8*, ugpr) realloc;
|
||||||
|
|
||||||
|
record ListDLC[T, S; growth] {
|
||||||
|
S capacity;
|
||||||
|
S size;
|
||||||
|
T[?]* data;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListDLC_remove: [T, S; growth]u0(ListDLC[T, S; growth]* this, S index) -> {
|
||||||
|
T* data0 = &((*((*this).data))[index]);
|
||||||
|
T* data1 = data0 + @sizeof T;
|
||||||
|
S sz = (*this).size;
|
||||||
|
(*this).size = (*this).size - 1;
|
||||||
|
loop {
|
||||||
|
if(index == sz) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*data0 = *data1;
|
||||||
|
|
||||||
|
data0 = data0 + @sizeof T;
|
||||||
|
data1 = data1 + @sizeof T;
|
||||||
|
index = index + 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
ListDLC_add: [T, S; growth]u0(ListDLC[T, S; growth]* this, T value) -> {
|
||||||
|
if((*this).size == (*this).capacity) {
|
||||||
|
S newcap = (*this).capacity + growth;
|
||||||
|
(*this).capacity = newcap;
|
||||||
|
(*this).data = realloc((*this).data, newcap * @sizeof T);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*((*this).data))[(*this).size] = value;
|
||||||
|
(*this).size = (*this).size + 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
43
examples/ListDLU.nct
Normal file
43
examples/ListDLU.nct
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* ListDLU: Dynamic, Linear growth, unmanaged
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Alloc;
|
||||||
|
|
||||||
|
record ListDLU[T, S; growth] {
|
||||||
|
S capacity;
|
||||||
|
S size;
|
||||||
|
T[?]* data;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListDLU_remove: [T, S; growth]u0(ListDLU[T, S; growth]* this, S index) -> {
|
||||||
|
T* data0 = &(*(*this).data)[index];
|
||||||
|
T* data1 = data0 + @sizeof T;
|
||||||
|
S sz = (*this).size;
|
||||||
|
(*this).size = (*this).size - 1;
|
||||||
|
loop {
|
||||||
|
if(index == sz) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*data0 = *data1;
|
||||||
|
|
||||||
|
data0 = data0 + @sizeof T;
|
||||||
|
data1 = data1 + @sizeof T;
|
||||||
|
index = index + 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
ListDLU_add: [T, S; growth]u0(ListDLU[T, S; growth]* this, Alloc *alloc, T value) -> {
|
||||||
|
if((*this).size == (*this).capacity) {
|
||||||
|
u32 newcap = (*this).capacity + growth;
|
||||||
|
(*this).capacity = newcap;
|
||||||
|
(*this).data = (*alloc).realloc((*alloc).userdata, (*this).data, newcap * @sizeof T);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*((*this).data))[(*this).size] = value;
|
||||||
|
(*this).size = (*this).size + 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
27
examples/ListS.nct
Normal file
27
examples/ListS.nct
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
record ListS[T, S; capacity] {
|
||||||
|
S size;
|
||||||
|
T[capacity] data;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListS_remove: [T, S; capacity]u0(ListS[T, S; capacity]* this, S index) -> {
|
||||||
|
T* data = &((*this).data[index]);
|
||||||
|
(*this).size = (*this).size - 1;
|
||||||
|
S sz = (*this).size;
|
||||||
|
loop {
|
||||||
|
if(index == sz) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*data = *(data + 1);
|
||||||
|
|
||||||
|
data = data + 1;
|
||||||
|
index = index + 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
ListS_add: [T, S; capacity]u0(ListS[T, S; capacity]* this, T value) -> {
|
||||||
|
(*this).data[(*this).size] = value;
|
||||||
|
(*this).size = (*this).size + 1;
|
||||||
|
return;
|
||||||
|
};
|
||||||
6
examples/LogicalAnd.nct
Normal file
6
examples/LogicalAnd.nct
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
u16 x = 0;
|
||||||
|
u16 y = 5;
|
||||||
|
|
||||||
|
if(x && y) {
|
||||||
|
u16 z = 9;
|
||||||
|
}
|
||||||
7
examples/LogicalAndOr.nct
Normal file
7
examples/LogicalAndOr.nct
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
u16 x = 0;
|
||||||
|
u16 y = 5;
|
||||||
|
u16 z = 9;
|
||||||
|
|
||||||
|
if(x && y || z) {
|
||||||
|
u16 w = 15;
|
||||||
|
}
|
||||||
6
examples/LogicalOr.nct
Normal file
6
examples/LogicalOr.nct
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
u16 x = 0;
|
||||||
|
u16 y = 5;
|
||||||
|
|
||||||
|
if(x || y) {
|
||||||
|
u16 z = 9;
|
||||||
|
}
|
||||||
132
examples/MapDQCaLDhS.nct
Normal file
132
examples/MapDQCaLDhS.nct
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* MapDQCOaLDhS: Dynamically-allocated, Quadratic growth, C allocator, Interleaved KV values, Open-addressing, Linear probing, Dynamic hash, Flag tombstones
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern u8*(u8*, ugpr) calloc;
|
||||||
|
|
||||||
|
record KVPair[K, V] {
|
||||||
|
K key;
|
||||||
|
V value;
|
||||||
|
}
|
||||||
|
|
||||||
|
record MapDQCOaLDhS[K, V, S] {
|
||||||
|
S(K*)* hash;
|
||||||
|
|
||||||
|
S capacity;
|
||||||
|
KVPair[K, V][?]* data;
|
||||||
|
u8[?]* occupied;
|
||||||
|
}
|
||||||
|
|
||||||
|
MapDQCOaLDhS_try_add: [K, V, S]u1(MapDQCOaLDhS[K, V, S]* this, K* key, V* value) -> {
|
||||||
|
if(this.capacity == 0) {
|
||||||
|
this.capacity = 64;
|
||||||
|
this.data = calloc(this.capacity, @sizeof KVPair[K, V]);
|
||||||
|
this.occupied = calloc(this.capacity, @sizeof((*this.occupied)[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
S capacity = this.capacity;
|
||||||
|
|
||||||
|
S start = this.hash(key);
|
||||||
|
start = start & (capacity - 1);
|
||||||
|
|
||||||
|
S index = start;
|
||||||
|
loop {
|
||||||
|
KVPair[K, V]* pair = &(*this.data)[index];
|
||||||
|
if(pair.key == *key || (*this.occupied)[index] == 0) {
|
||||||
|
pair.key = *key;
|
||||||
|
pair.value = *value;
|
||||||
|
(*this.occupied)[index] = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
index = (index + 1) & (capacity - 1);
|
||||||
|
if(index == start) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MapDQCOaLDhS_expand: [K, V, S]u1(MapDQCOaLDhS[K, V, S]* this) -> {
|
||||||
|
S capacity = this.capacity;
|
||||||
|
KVPair[K, V][?]* old_data = this.data;
|
||||||
|
u8[?]* old_occupied = this.occupied;
|
||||||
|
|
||||||
|
S new_capacity = capacity * 2;
|
||||||
|
this.capacity = new_capacity;
|
||||||
|
this.data = calloc(new_capacity, @sizeof KVPair[K, V]);
|
||||||
|
this.occupied = calloc(new_capacity, @sizeof((*this.occupied)[0]));
|
||||||
|
|
||||||
|
S i = 0;
|
||||||
|
loop {
|
||||||
|
if(i == capacity) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if((*old_occupied)[i] != 0) {
|
||||||
|
KVPair[K, V]* pair = &((*old_data)[i]);
|
||||||
|
MapDQCOaLDhS_try_add[K, V, S](this, &pair.key, &pair.value);
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
MapDQCOaLDhS_add: [K, V, S]u1(MapDQCOaLDhS[K, V, S]* this, K* key, V* value) -> {
|
||||||
|
loop {
|
||||||
|
if(MapDQCOaLDhS_try_add[K, V, S](this, key, value) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(MapDQCOaLDhS_expand[K, V, S](this) == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MapDQCOaLDhS_get: [K, V, S]V*(MapDQCOaLDhS[K, V, S]* this, K* key) -> {
|
||||||
|
S capacity = this.capacity;
|
||||||
|
|
||||||
|
if(capacity == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
S start = this.hash(key);
|
||||||
|
start = start & (capacity - 1);
|
||||||
|
|
||||||
|
S index = start;
|
||||||
|
loop {
|
||||||
|
KVPair[K, V]* pair = &((*this.data)[index]);
|
||||||
|
if(pair.key == *key) {
|
||||||
|
if((*this.occupied)[index] == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return &pair.value;
|
||||||
|
}
|
||||||
|
index = (index + 1) & (capacity - 1);
|
||||||
|
if(index == start) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
zero_hash: ugpr(ugpr* val) -> {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
@instantiate MapDQCOaLDhS_try_add[ugpr, ugpr, ugpr];
|
||||||
|
@instantiate MapDQCOaLDhS_expand[ugpr, ugpr, ugpr];
|
||||||
|
@instantiate MapDQCOaLDhS_add[ugpr, ugpr, ugpr];
|
||||||
|
@instantiate MapDQCOaLDhS_get[ugpr, ugpr, ugpr];
|
||||||
|
|
||||||
|
main: u0() -> {
|
||||||
|
map.hash = &zero_hash;
|
||||||
|
|
||||||
|
ugpr test_key = 5;
|
||||||
|
MapDQCOaLDhS_get[ugpr, ugpr, ugpr](&map, &test_key);
|
||||||
|
MapDQCOaLDhS_add[ugpr, ugpr, ugpr](&map, &test_key, &test_value);
|
||||||
|
MapDQCOaLDhS_get[ugpr, ugpr, ugpr](&map, &test_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
|
||||||
|
@section(".data");
|
||||||
|
MapDQCOaLDhS[ugpr, ugpr, ugpr; 32] map:;
|
||||||
|
ugpr test_value: 10;
|
||||||
77
examples/MapSOaLDhS.nct
Normal file
77
examples/MapSOaLDhS.nct
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* SIOaLpDhFt: Statically-allocated, Interleaved KV values, Open-addressing, Linear probing, Dynamic hash, Flag tombstones
|
||||||
|
*/
|
||||||
|
|
||||||
|
record KVPair[K, V] {
|
||||||
|
K key;
|
||||||
|
V value;
|
||||||
|
}
|
||||||
|
|
||||||
|
record MapSIOaLpDhFt[K, V, S; capacity] {
|
||||||
|
S(K*)* hash;
|
||||||
|
KVPair[capacity] data;
|
||||||
|
u8[capacity] occupied;
|
||||||
|
}
|
||||||
|
|
||||||
|
MapSIOaLpDhFt_add: [K, V, S; capacity]u1(MapSIOaLpDhFt[K, V, S; capacity]* this, K* key, V* value) -> {
|
||||||
|
S start = this.hash(key);
|
||||||
|
start = start & (capacity - 1);
|
||||||
|
|
||||||
|
S index = start;
|
||||||
|
loop {
|
||||||
|
KVPair[K, V]* pair = &this.data[index];
|
||||||
|
if(pair.key == *key || this.occupied[index] == 0) {
|
||||||
|
pair.key = *key;
|
||||||
|
pair.value = *value;
|
||||||
|
this.occupied[index] = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
index = (index + 1) & (capacity - 1);
|
||||||
|
if(index == start) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MapSIOaLpDhFt_get: [K, V, S; capacity]V*(MapSIOaLpDhFt[K, V, S; capacity]* this, K* key) -> {
|
||||||
|
S start = this.hash(key);
|
||||||
|
start = start & (capacity - 1);
|
||||||
|
|
||||||
|
S index = start;
|
||||||
|
loop {
|
||||||
|
KVPair[K, V]* pair = &this.data[index];
|
||||||
|
if(pair.key == *key) {
|
||||||
|
if(this.occupied[index] == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return &pair.value;
|
||||||
|
}
|
||||||
|
index = (index + 1) & (capacity - 1);
|
||||||
|
if(index == start) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
zero_hash: ugpr(ugpr* val) -> {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
@instantiate MapSIOaLpDhFt_add[ugpr, ugpr, ugpr; 32];
|
||||||
|
@instantiate MapSIOaLpDhFt_get[ugpr, ugpr, ugpr; 32];
|
||||||
|
|
||||||
|
main: u0() -> {
|
||||||
|
map.hash = &zero_hash;
|
||||||
|
|
||||||
|
MapSIOaLpDhFt_get[ugpr, ugpr, ugpr; 32](&map, &test_key);
|
||||||
|
MapSIOaLpDhFt_add[ugpr, ugpr, ugpr; 32](&map, &test_key, &test_value);
|
||||||
|
MapSIOaLpDhFt_get[ugpr, ugpr, ugpr; 32](&map, &test_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
|
||||||
|
@section(".data");
|
||||||
|
MapSIOaLpDhFt[ugpr, ugpr, ugpr; 32] map:;
|
||||||
|
|
||||||
|
ugpr test_value: 10;
|
||||||
|
ugpr test_key: 5;
|
||||||
9
examples/ReachingDefinitions.nct
Normal file
9
examples/ReachingDefinitions.nct
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
u16 x = 0;
|
||||||
|
|
||||||
|
x = 5;
|
||||||
|
|
||||||
|
if(x == 2) {
|
||||||
|
x = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = 4;
|
||||||
14
examples/ScalarReplacementOptimization.nct
Normal file
14
examples/ScalarReplacementOptimization.nct
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
record X {
|
||||||
|
u32 a;
|
||||||
|
u32 b;
|
||||||
|
}
|
||||||
|
|
||||||
|
main: u0() -> {
|
||||||
|
X x;
|
||||||
|
x.a = 5;
|
||||||
|
x.b = 6;
|
||||||
|
|
||||||
|
u32 c = x.a + x.b;
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
43
examples/UDPEcho.nct
Normal file
43
examples/UDPEcho.nct
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
@section(".text");
|
||||||
|
|
||||||
|
record sockaddr {
|
||||||
|
0: u16 family;
|
||||||
|
2: u16 port;
|
||||||
|
4: u32 addr;
|
||||||
|
8: u32 zero0;
|
||||||
|
12: u32 zero1;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern u32(u32, u32, u32) socket;
|
||||||
|
extern u32(u32, sockaddr*, u32) bind;
|
||||||
|
extern u32(u32, u8*, u32, u32, u8*, u32*) recvfrom;
|
||||||
|
extern u32(u32, u8*, u32, u32, u8*, u32) sendto;
|
||||||
|
|
||||||
|
MY_SOCKET = socket(2, 2, 0);
|
||||||
|
|
||||||
|
sockaddr sa;
|
||||||
|
sa.family = 2; /* AF_INET */
|
||||||
|
sa.port = 43105; /* 25000 in net-endian */
|
||||||
|
sa.addr = 0; /* Bind to all */
|
||||||
|
sa.zero0 = 0;
|
||||||
|
sa.zero1 = 0;
|
||||||
|
|
||||||
|
bind(MY_SOCKET, &sa, 16);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
|
||||||
|
OTHER_SIDE_LEN = 64;
|
||||||
|
u32 msglength = recvfrom(MY_SOCKET, &BUFFER, 512, 0, &OTHER_SIDE, &OTHER_SIDE_LEN);
|
||||||
|
|
||||||
|
sendto(MY_SOCKET, &BUFFER, msglength, 0, &OTHER_SIDE, OTHER_SIDE_LEN);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@section(".data");
|
||||||
|
|
||||||
|
u32 MY_SOCKET:;
|
||||||
|
|
||||||
|
u32 OTHER_SIDE_LEN:;
|
||||||
|
u8[64] OTHER_SIDE:;
|
||||||
|
|
||||||
|
u8[512] BUFFER:;
|
||||||
18
examples/UserListDLC.nct
Normal file
18
examples/UserListDLC.nct
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use ListDLC;
|
||||||
|
|
||||||
|
@section(".text");
|
||||||
|
|
||||||
|
@instantiate ListDLC_remove[u16, u16; 9];
|
||||||
|
@instantiate ListDLC_add[u16, u16; 9];
|
||||||
|
|
||||||
|
main: u0() -> {
|
||||||
|
ListDLC[u16, u16; 9] list;
|
||||||
|
ListDLC_add[u16, u16; 9](&list, 1234);
|
||||||
|
ListDLC_add[u16, u16; 9](&list, 4321);
|
||||||
|
ListDLC_add[u16, u16; 9](&list, 7777);
|
||||||
|
ListDLC_add[u16, u16; 9](&list, 6969);
|
||||||
|
ListDLC_remove[u16, u16; 9](&list, 1);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
@section(".bss");
|
||||||
32
examples/UserListDLU.nct
Normal file
32
examples/UserListDLU.nct
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
use ListDLU;
|
||||||
|
|
||||||
|
@section(".text");
|
||||||
|
|
||||||
|
@instantiate ListDLU_remove[u32, u32; 9];
|
||||||
|
@instantiate ListDLU_add[u32, u32; 9];
|
||||||
|
|
||||||
|
extern u8*(u8* ptr, u32 sz) realloc;
|
||||||
|
libc_realloc: u8*(u8* userdata, u8* ptr, u32 sz) -> {
|
||||||
|
return realloc(ptr, sz);
|
||||||
|
};
|
||||||
|
|
||||||
|
main: u0() -> {
|
||||||
|
Alloc libc;
|
||||||
|
libc.realloc = &libc_realloc;
|
||||||
|
|
||||||
|
ListDLU[u32, u32; 9] list;
|
||||||
|
list.capacity = 0;
|
||||||
|
list.size = 0;
|
||||||
|
list.data = 0;
|
||||||
|
|
||||||
|
ListDLU_add[u32, u32; 9](&list, &libc, 1234);
|
||||||
|
ListDLU_add[u32, u32; 9](&list, &libc, 4321);
|
||||||
|
ListDLU_add[u32, u32; 9](&list, &libc, 7777);
|
||||||
|
ListDLU_add[u32, u32; 9](&list, &libc, 6969);
|
||||||
|
|
||||||
|
ListDLU_remove[u32, u32; 9](&list, 1);
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
@section(".bss");
|
||||||
@@ -30,12 +30,10 @@ loop {
|
|||||||
data[dataPtr] = data[dataPtr] - 1;
|
data[dataPtr] = data[dataPtr] - 1;
|
||||||
}
|
}
|
||||||
if(code[codePtr] == 46) {
|
if(code[codePtr] == 46) {
|
||||||
u32 z = &data + dataPtr;
|
write(1, &data + dataPtr, 1);
|
||||||
write(1, z, 1);
|
|
||||||
}
|
}
|
||||||
if(code[codePtr] == 44) {
|
if(code[codePtr] == 44) {
|
||||||
u32 z = &data + dataPtr;
|
read(0, &data + dataPtr, 1);
|
||||||
read(0, z, 1);
|
|
||||||
}
|
}
|
||||||
if(code[codePtr] == 91) {
|
if(code[codePtr] == 91) {
|
||||||
if(data[dataPtr] == 0) {
|
if(data[dataPtr] == 0) {
|
||||||
@@ -52,8 +50,7 @@ loop {
|
|||||||
}
|
}
|
||||||
codePtr = codePtr + 1;
|
codePtr = codePtr + 1;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
if(data[dataPtr] != 0) {
|
|
||||||
stckPtr = stckPtr + 1;
|
stckPtr = stckPtr + 1;
|
||||||
stck[stckPtr] = codePtr;
|
stck[stckPtr] = codePtr;
|
||||||
}
|
}
|
||||||
@@ -64,8 +61,7 @@ loop {
|
|||||||
if(code[codePtr] == 93) {
|
if(code[codePtr] == 93) {
|
||||||
if(data[dataPtr] == 0) {
|
if(data[dataPtr] == 0) {
|
||||||
stckPtr = stckPtr - 1;
|
stckPtr = stckPtr - 1;
|
||||||
}
|
} else {
|
||||||
if(data[dataPtr] != 0) {
|
|
||||||
codePtr = stck[stckPtr];
|
codePtr = stck[stckPtr];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,7 +71,3 @@ loop {
|
|||||||
|
|
||||||
codePtr = codePtr + 1;
|
codePtr = codePtr + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
codePtr;
|
|
||||||
dataPtr;
|
|
||||||
stckPtr;
|
|
||||||
11
examples/bit-rounding.nct
Normal file
11
examples/bit-rounding.nct
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
u32 x: 123;
|
||||||
|
u33 y: 5;
|
||||||
|
|
||||||
|
u3* o = 5000;
|
||||||
|
u32 z = *o;
|
||||||
|
u3 p = *(o + z);
|
||||||
|
u3 *g = o - z;
|
||||||
|
p = *g;
|
||||||
|
|
||||||
|
u32 c = 566;
|
||||||
|
u16 j = c;
|
||||||
48
examples/countwords.nct
Normal file
48
examples/countwords.nct
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
@section(".data");
|
||||||
|
|
||||||
|
local u8[33] finalstring: "The amount of words in stdin is: ";
|
||||||
|
|
||||||
|
local u8[4] answer: "0000";
|
||||||
|
|
||||||
|
@section(".text");
|
||||||
|
|
||||||
|
extern s32() getchar;
|
||||||
|
extern s32(s32, u8*, u32) write;
|
||||||
|
|
||||||
|
increase: u0() -> {
|
||||||
|
answer[3] = answer[3] + 1;
|
||||||
|
if(answer[3] > 57) {
|
||||||
|
answer[3] = 48;
|
||||||
|
answer[2] = answer[2] + 1;
|
||||||
|
if(answer[2] > 57) {
|
||||||
|
answer[2] = 48;
|
||||||
|
answer[1] = answer[1] + 1;
|
||||||
|
if(answer[1] > 57) {
|
||||||
|
answer[1] = 48;
|
||||||
|
answer[0] = answer[0] + 1;
|
||||||
|
if(answer[0] > 57) {
|
||||||
|
answer[0] = 48;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
main: u0() -> {
|
||||||
|
loop {
|
||||||
|
s32 c = getchar();
|
||||||
|
if(c == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(c == 32) {
|
||||||
|
increase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write(1, &finalstring, 33);
|
||||||
|
write(1, &answer, 4);
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
6
examples/funcdefs.nct
Normal file
6
examples/funcdefs.nct
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
fibonacci: u16(u16 n) -> {
|
||||||
|
if(n <= 1) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
return fibonacci(n - 1) + fibonacci(n - 2);
|
||||||
|
};
|
||||||
12
examples/functions.nct
Normal file
12
examples/functions.nct
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
extern s32() getchar;
|
||||||
|
extern u0(s32) putchar;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
s32 z = 5;
|
||||||
|
s32 a = getchar();
|
||||||
|
if(a == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
putchar(a);
|
||||||
|
putchar(z);
|
||||||
|
}
|
||||||
7
examples/if.nct
Normal file
7
examples/if.nct
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
u16 x: 5;
|
||||||
|
|
||||||
|
if(x != 0) {
|
||||||
|
x = 2;
|
||||||
|
} else {
|
||||||
|
x = 5;
|
||||||
|
}
|
||||||
11
examples/loopregalloc.nct
Normal file
11
examples/loopregalloc.nct
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
u8 a;
|
||||||
|
|
||||||
|
a = 2;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
a = 3;
|
||||||
|
|
||||||
|
u8 b = 1;
|
||||||
|
|
||||||
|
u8 c = 2;
|
||||||
|
}
|
||||||
16
examples/lvl1localrecord.nct
Normal file
16
examples/lvl1localrecord.nct
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
record sockaddr {
|
||||||
|
u32 family;
|
||||||
|
u16 port;
|
||||||
|
u32 addr;
|
||||||
|
u32 zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
@section(".data");
|
||||||
|
|
||||||
|
@section(".text");
|
||||||
|
|
||||||
|
sockaddr sa;
|
||||||
|
sa.family = 5;
|
||||||
|
sa.port = 43105;
|
||||||
|
sa.addr = 16rC0A80001;
|
||||||
|
sa.zero = 0;
|
||||||
17
examples/lvl1symbolrecord.nct
Normal file
17
examples/lvl1symbolrecord.nct
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
record sockaddr {
|
||||||
|
u32 family;
|
||||||
|
u16 port;
|
||||||
|
u32 addr;
|
||||||
|
u32 zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
@section(".data");
|
||||||
|
|
||||||
|
sockaddr sa:;
|
||||||
|
|
||||||
|
@section(".text");
|
||||||
|
|
||||||
|
sa.family = 5;
|
||||||
|
sa.port = 43105;
|
||||||
|
sa.addr = 16rC0A80001;
|
||||||
|
sa.zero = 0;
|
||||||
16
examples/lvl2localrecord.nct
Normal file
16
examples/lvl2localrecord.nct
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
record A {
|
||||||
|
u32 x;
|
||||||
|
u8 z;
|
||||||
|
}
|
||||||
|
|
||||||
|
record B {
|
||||||
|
0: u32 x;
|
||||||
|
7: A a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@section(".text");
|
||||||
|
|
||||||
|
B rec;
|
||||||
|
rec.x = 5;
|
||||||
|
rec.a.x = 10;
|
||||||
|
rec.a.z = 9;
|
||||||
19
examples/lvl2symbolrecord.nct
Normal file
19
examples/lvl2symbolrecord.nct
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
record A {
|
||||||
|
u32 x;
|
||||||
|
u8 z;
|
||||||
|
}
|
||||||
|
|
||||||
|
record B {
|
||||||
|
u32 x;
|
||||||
|
A a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@section(".data");
|
||||||
|
|
||||||
|
B rec:;
|
||||||
|
|
||||||
|
@section(".text");
|
||||||
|
|
||||||
|
rec.x = 5;
|
||||||
|
rec.a.x = 10;
|
||||||
|
rec.a.z = 9;
|
||||||
@@ -19,4 +19,4 @@ loop {}
|
|||||||
u8[19] string: "Hello from Nectar!\0";
|
u8[19] string: "Hello from Nectar!\0";
|
||||||
|
|
||||||
@align(510);
|
@align(510);
|
||||||
u16 bootsig: 16rAA55;
|
u16 bootsig: 16rAA55;
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
u16 a = 12;
|
u16 a = 12;
|
||||||
u16 b = a & 6;
|
u16 b = a & 6;
|
||||||
u16 c = b ^ a | 3;
|
u16 c = b ^ a | 3;
|
||||||
u16 d = 11 * c;
|
u16 o = 5;
|
||||||
|
u16 d = o * 2;
|
||||||
|
|
||||||
if(a) {
|
if(a == 0) {
|
||||||
u16 e = b + c + d;
|
u16 e = b + c + d;
|
||||||
}
|
}
|
||||||
23
examples/parsenum.nct
Normal file
23
examples/parsenum.nct
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
@section(".data");
|
||||||
|
|
||||||
|
u8 buf: 0;
|
||||||
|
|
||||||
|
@section(".text");
|
||||||
|
|
||||||
|
extern u0(u32) exit;
|
||||||
|
extern s32(u32, u8*, u32) read;
|
||||||
|
|
||||||
|
_start: u0() -> {
|
||||||
|
u16 value = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if(read(0, &buf, 1) != 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf = buf - 48;
|
||||||
|
value = value * 10;
|
||||||
|
value = value + buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(value);
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
u8 a = 5;
|
u8 a = 5;
|
||||||
if(a) {
|
if(a != 0) {
|
||||||
u8 a = 10; /* Should not cause scoping errors. */
|
u8 a = 10; /* Should not cause scoping errors. */
|
||||||
}
|
}
|
||||||
u8 b = 15; /* `a` in the if statement scope should be free'd. */
|
u8 b = 15; /* `a` in the if statement scope should be free'd. */
|
||||||
6
examples/spill.nct
Normal file
6
examples/spill.nct
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
u8 a = 4;
|
||||||
|
u8 b = 1;
|
||||||
|
u8 c = 9;
|
||||||
|
u8 d = 5;
|
||||||
|
u8 e = 22;
|
||||||
|
u8 f = a + b + c + d + e;
|
||||||
1
examples/stack.nct
Normal file
1
examples/stack.nct
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@stack = @stack - 123;
|
||||||
63
src/ast.c
63
src/ast.c
@@ -1,63 +0,0 @@
|
|||||||
#include"ast.h"
|
|
||||||
|
|
||||||
#include<stdint.h>
|
|
||||||
#include<string.h>
|
|
||||||
#include<stdlib.h>
|
|
||||||
|
|
||||||
int BINOP_COMMUTATIVE[] = {
|
|
||||||
[BINOP_ADD] = 1,
|
|
||||||
[BINOP_SUB] = 0,
|
|
||||||
[BINOP_MUL] = 1,
|
|
||||||
[BINOP_DIV] = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
AST *ast_expression_optimize(AST *ast) {
|
|
||||||
return ast;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ast_expression_equal(AST *a, AST *b) {
|
|
||||||
if(a->nodeKind != b->nodeKind) return 0;
|
|
||||||
|
|
||||||
if(a->nodeKind == AST_EXPR_PRIMITIVE) {
|
|
||||||
return a->exprPrim.val == b->exprPrim.val;
|
|
||||||
} else if(a->nodeKind == AST_EXPR_VAR) {
|
|
||||||
return a->exprVar.thing == b->exprVar.thing;
|
|
||||||
} else if(a->nodeKind == AST_EXPR_UNARY_OP) {
|
|
||||||
return a->exprUnOp.operator == b->exprUnOp.operator && ast_expression_equal(a->exprUnOp.operand, b->exprUnOp.operand);
|
|
||||||
} else if(a->nodeKind == AST_EXPR_BINARY_OP) {
|
|
||||||
return a->exprBinOp.operator == b->exprBinOp.operator && ast_expression_equal(a->exprBinOp.operands[0], b->exprBinOp.operands[0]) && ast_expression_equal(a->exprBinOp.operands[1], b->exprBinOp.operands[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... Ew
|
|
||||||
int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) {
|
|
||||||
const AST *s = chunk->chunk.statementFirst;
|
|
||||||
|
|
||||||
while(s) {
|
|
||||||
if(s == s1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(s == s2) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(s->nodeKind == AST_STMT_IF) {
|
|
||||||
int i = ast_stmt_is_after(s->stmtIf.then, s1, s2);
|
|
||||||
if(i != -1) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
} else if(s->nodeKind == AST_STMT_LOOP) {
|
|
||||||
int i = ast_stmt_is_after(s->stmtLoop.body, s1, s2);
|
|
||||||
if(i != -1) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s = s->statement.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
259
src/ast.h
259
src/ast.h
@@ -1,259 +0,0 @@
|
|||||||
#ifndef NCTREF_AST_H
|
|
||||||
#define NCTREF_AST_H
|
|
||||||
|
|
||||||
#include"types.h"
|
|
||||||
#include"lexer.h"
|
|
||||||
#include"vartable.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
AST_CHUNK,
|
|
||||||
AST_STMT_DECL,
|
|
||||||
AST_TYPE_IDENTIFIER,
|
|
||||||
AST_EXPR_PRIMITIVE,
|
|
||||||
AST_STMT_IF,
|
|
||||||
AST_EXPR_BINARY_OP,
|
|
||||||
AST_EXPR_VAR,
|
|
||||||
AST_TYPE_POINTER,
|
|
||||||
AST_EXPR_UNARY_OP,
|
|
||||||
AST_STMT_LOOP,
|
|
||||||
AST_STMT_BREAK,
|
|
||||||
AST_STMT_CONTINUE,
|
|
||||||
AST_EXPR_CALL,
|
|
||||||
AST_STMT_EXPR,
|
|
||||||
AST_STMT_ASSIGN,
|
|
||||||
AST_STMT_EXT_ALIGN,
|
|
||||||
AST_EXPR_STRING_LITERAL,
|
|
||||||
AST_EXPR_CAST,
|
|
||||||
AST_EXPR_ARRAY,
|
|
||||||
AST_STMT_EXT_ORG,
|
|
||||||
AST_STMT_EXT_SECTION,
|
|
||||||
} ASTKind;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
BINOP_ADD = 0,
|
|
||||||
BINOP_SUB = 1,
|
|
||||||
BINOP_BITWISE_AND = 2,
|
|
||||||
BINOP_BITWISE_OR = 3,
|
|
||||||
BINOP_BITWISE_XOR = 4,
|
|
||||||
BINOP_SIMPLES = 5,
|
|
||||||
BINOP_MUL = 5,
|
|
||||||
BINOP_DIV = 6,
|
|
||||||
|
|
||||||
BINOP_EQUAL = 7,
|
|
||||||
BINOP_NEQUAL = 8,
|
|
||||||
|
|
||||||
BINOP_WTF = 999,
|
|
||||||
} BinaryOp;
|
|
||||||
extern int BINOP_COMMUTATIVE[];
|
|
||||||
|
|
||||||
static inline int binop_is_comparison(BinaryOp op) {
|
|
||||||
return op == BINOP_EQUAL || op == BINOP_NEQUAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline BinaryOp binop_comp_opposite(BinaryOp op) {
|
|
||||||
if(op == BINOP_EQUAL) {
|
|
||||||
return BINOP_NEQUAL;
|
|
||||||
} else if(op == BINOP_NEQUAL) {
|
|
||||||
return BINOP_EQUAL;
|
|
||||||
}
|
|
||||||
return BINOP_WTF;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
UNOP_DEREF = 0,
|
|
||||||
UNOP_NEGATE = 1,
|
|
||||||
UNOP_BITWISE_NOT = 2,
|
|
||||||
UNOP_REF = 3,
|
|
||||||
} UnaryOp;
|
|
||||||
|
|
||||||
union AST;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTKind nodeKind;
|
|
||||||
Type *type;
|
|
||||||
} ASTExpr;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTExpr;
|
|
||||||
|
|
||||||
int val;
|
|
||||||
} ASTExprPrimitive;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTExpr;
|
|
||||||
|
|
||||||
union AST *operands[2];
|
|
||||||
BinaryOp operator;
|
|
||||||
} ASTExprBinaryOp;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTExpr;
|
|
||||||
|
|
||||||
UnaryOp operator;
|
|
||||||
union AST *operand;
|
|
||||||
} ASTExprUnaryOp;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTExpr;
|
|
||||||
|
|
||||||
VarTableEntry *thing;
|
|
||||||
} ASTExprVar;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTExpr;
|
|
||||||
|
|
||||||
union AST *what;
|
|
||||||
|
|
||||||
union AST **args;
|
|
||||||
} ASTExprCall;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTExpr;
|
|
||||||
|
|
||||||
size_t length;
|
|
||||||
char *data;
|
|
||||||
} ASTExprStringLiteral;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTKind nodeKind;
|
|
||||||
|
|
||||||
size_t size;
|
|
||||||
} ASTType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTType;
|
|
||||||
|
|
||||||
Token identifier;
|
|
||||||
} ASTTypeIdentifier;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTType;
|
|
||||||
|
|
||||||
union AST *child;
|
|
||||||
int levels;
|
|
||||||
} ASTTypePointer;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTKind nodeKind;
|
|
||||||
union AST *next;
|
|
||||||
} ASTStmt;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTStmt;
|
|
||||||
|
|
||||||
VarTableEntry *thing;
|
|
||||||
|
|
||||||
union AST *expression;
|
|
||||||
} ASTStmtDecl;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTKind nodeKind;
|
|
||||||
|
|
||||||
/* Flattened variable array for global register allocation */
|
|
||||||
size_t varCount;
|
|
||||||
VarTableEntry **vars;
|
|
||||||
|
|
||||||
union AST *statementFirst;
|
|
||||||
union AST *statementLast;
|
|
||||||
} ASTChunk;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTStmt;
|
|
||||||
|
|
||||||
union AST *expression;
|
|
||||||
|
|
||||||
union AST *then;
|
|
||||||
} ASTStmtIf;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTStmt;
|
|
||||||
|
|
||||||
union AST *body;
|
|
||||||
} ASTStmtLoop;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTStmt;
|
|
||||||
} ASTStmtBreak;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTStmt;
|
|
||||||
} ASTStmtContinue;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTStmt;
|
|
||||||
|
|
||||||
union AST *expr;
|
|
||||||
} ASTStmtExpr;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTStmt;
|
|
||||||
|
|
||||||
union AST *what;
|
|
||||||
union AST *to;
|
|
||||||
} ASTStmtAssign;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTStmt;
|
|
||||||
|
|
||||||
int val;
|
|
||||||
} ASTStmtExtAlign;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTExpr;
|
|
||||||
|
|
||||||
union AST *what;
|
|
||||||
Type *to;
|
|
||||||
|
|
||||||
char reinterpretation; /* 1 = as, 0 = to */
|
|
||||||
} ASTExprCast;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTExpr;
|
|
||||||
|
|
||||||
union AST **items;
|
|
||||||
} ASTExprArray;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTStmt;
|
|
||||||
|
|
||||||
size_t val;
|
|
||||||
} ASTStmtExtOrg;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
ASTStmt;
|
|
||||||
|
|
||||||
Token name;
|
|
||||||
} ASTStmtExtSection;
|
|
||||||
|
|
||||||
typedef union AST {
|
|
||||||
ASTKind nodeKind;
|
|
||||||
|
|
||||||
ASTChunk chunk;
|
|
||||||
ASTStmt statement;
|
|
||||||
ASTStmtDecl stmtDecl;
|
|
||||||
ASTStmtIf stmtIf;
|
|
||||||
ASTStmtLoop stmtLoop;
|
|
||||||
ASTStmtBreak stmtBreak;
|
|
||||||
ASTStmtContinue stmtContinue;
|
|
||||||
ASTStmtExpr stmtExpr;
|
|
||||||
ASTStmtAssign stmtAssign;
|
|
||||||
ASTExpr expression;
|
|
||||||
ASTExprPrimitive exprPrim;
|
|
||||||
ASTExprBinaryOp exprBinOp;
|
|
||||||
ASTExprUnaryOp exprUnOp;
|
|
||||||
ASTExprVar exprVar;
|
|
||||||
ASTExprCall exprCall;
|
|
||||||
ASTStmtExtAlign stmtExtAlign;
|
|
||||||
ASTExprStringLiteral exprStrLit;
|
|
||||||
ASTExprCast exprCast;
|
|
||||||
ASTExprArray exprArray;
|
|
||||||
ASTStmtExtOrg stmtExtOrg;
|
|
||||||
ASTStmtExtSection stmtExtSection;
|
|
||||||
} AST;
|
|
||||||
|
|
||||||
AST *ast_expression_optimize(AST*);
|
|
||||||
int ast_expression_equal(AST*, AST*);
|
|
||||||
|
|
||||||
int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
352
src/ast/ast.c
Normal file
352
src/ast/ast.c
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
#include"ast.h"
|
||||||
|
|
||||||
|
#include<stdint.h>
|
||||||
|
#include<string.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<assert.h>
|
||||||
|
#include<stdarg.h>
|
||||||
|
#include"ntc.h"
|
||||||
|
#include"reporting.h"
|
||||||
|
#include"utils.h"
|
||||||
|
#include"x86/arch.h"
|
||||||
|
|
||||||
|
const char *AST_KIND_STR[] = { AST_KINDS(GEN_STRI) };
|
||||||
|
|
||||||
|
void generic_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, void *ud, GenericVisitorHandler preHandler, GenericVisitorHandler postHandler) {
|
||||||
|
if(preHandler) preHandler(nptr, stmt, stmtPrev, chu, tlc, ud);
|
||||||
|
|
||||||
|
AST *n = *nptr;
|
||||||
|
|
||||||
|
if(n->nodeKind == AST_CHUNK) {
|
||||||
|
AST *sPrev = NULL;
|
||||||
|
AST **s = &n->chunk.statementFirst;
|
||||||
|
while(*s) {
|
||||||
|
generic_visitor(s, *s, sPrev, n, tlc, ud, preHandler, postHandler);
|
||||||
|
|
||||||
|
sPrev = *s;
|
||||||
|
s = &sPrev->statement.next;
|
||||||
|
}
|
||||||
|
} else if(n->nodeKind == AST_STMT_ASSIGN) {
|
||||||
|
generic_visitor(&n->stmtAssign.what, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
if(n->stmtAssign.to) {
|
||||||
|
generic_visitor(&n->stmtAssign.to, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
}
|
||||||
|
} else if(n->nodeKind == AST_STMT_IF) {
|
||||||
|
generic_visitor(&n->stmtIf.expression, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
generic_visitor(&n->stmtIf.then, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
if(n->stmtIf.elss) {
|
||||||
|
generic_visitor(&n->stmtIf.elss, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
}
|
||||||
|
} else if(n->nodeKind == AST_STMT_LOOP) {
|
||||||
|
generic_visitor(&n->stmtLoop.body, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
} else if(n->nodeKind == AST_STMT_BREAK) {
|
||||||
|
} else if(n->nodeKind == AST_STMT_CONTINUE) {
|
||||||
|
} else if(n->nodeKind == AST_STMT_EXT_ALIGN) {
|
||||||
|
} else if(n->nodeKind == AST_STMT_DECL) {
|
||||||
|
if(n->stmtDecl.expression) {
|
||||||
|
generic_visitor(&n->stmtDecl.expression, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
}
|
||||||
|
} else if(n->nodeKind == AST_STMT_EXPR) {
|
||||||
|
generic_visitor(&n->stmtExpr.expr, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
} else if(n->nodeKind == AST_STMT_EXT_ORG) {
|
||||||
|
} else if(n->nodeKind == AST_SECTION) {
|
||||||
|
generic_visitor(&n->section.tlc, stmt, stmtPrev, n->section.tlc, n->section.tlc, ud, preHandler, postHandler);
|
||||||
|
if(n->section.next) {
|
||||||
|
generic_visitor(&n->section.next, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
}
|
||||||
|
} else if(n->nodeKind == AST_STMT_RETURN) {
|
||||||
|
if(n->stmtReturn.val) {
|
||||||
|
generic_visitor(&n->stmtReturn.val, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
}
|
||||||
|
} else if(n->nodeKind == AST_EXPR_BINARY_OP) {
|
||||||
|
if(n->exprBinOp.operands[0]) {
|
||||||
|
generic_visitor(&n->exprBinOp.operands[0], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
}
|
||||||
|
if(n->exprBinOp.operands[1]) {
|
||||||
|
generic_visitor(&n->exprBinOp.operands[1], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
}
|
||||||
|
} else if(n->nodeKind == AST_EXPR_CALL) {
|
||||||
|
generic_visitor(&n->exprCall.what, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < n->exprCall.what->expression.type->pointer.of->function.argCount; i++) {
|
||||||
|
generic_visitor(&n->exprCall.args[i], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
}
|
||||||
|
} else if(n->nodeKind == AST_EXPR_CAST) {
|
||||||
|
generic_visitor(&n->exprCast.what, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
} else if(n->nodeKind == AST_EXPR_FUNC) {
|
||||||
|
generic_visitor(&n->exprFunc.chunk, NULL, NULL, n->exprFunc.chunk, n->exprFunc.chunk, ud, preHandler, postHandler);
|
||||||
|
} else if(n->nodeKind == AST_EXPR_UNARY_OP) {
|
||||||
|
generic_visitor(&n->exprUnOp.operand, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
} else if(n->nodeKind == AST_EXPR_VAR) {
|
||||||
|
} else if(n->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
} else if(n->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
} else if(n->nodeKind == AST_EXPR_STRING_LITERAL) {
|
||||||
|
} else if(n->nodeKind == AST_EXPR_ARRAY) {
|
||||||
|
assert(n->expression.type->type == TYPE_TYPE_ARRAY);
|
||||||
|
assert(n->expression.type->array.length != 0);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < n->expression.type->array.length; i++) {
|
||||||
|
generic_visitor(&n->exprArray.items[i], stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
}
|
||||||
|
} else if(n->nodeKind == AST_EXPR_EXT_SALLOC) {
|
||||||
|
} else if(n->nodeKind == AST_EXPR_DOT) {
|
||||||
|
generic_visitor(&n->exprDot.a, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
} else if(n->nodeKind == AST_EXPR_EXT_SIZEOF) {
|
||||||
|
if(n->exprExtSizeOf.ofExpr) {
|
||||||
|
generic_visitor(&n->exprExtSizeOf.ofExpr, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
}
|
||||||
|
} else if(n->nodeKind == AST_STMT_JUMP) {
|
||||||
|
if(n->stmtJump.condition) {
|
||||||
|
generic_visitor(&n->stmtJump.condition, stmt, stmtPrev, chu, tlc, ud, preHandler, postHandler);
|
||||||
|
}
|
||||||
|
} else if(n->nodeKind == AST_STMT_LABEL) {
|
||||||
|
} else if(n->nodeKind == AST_EXPR_NULL) {
|
||||||
|
} else {
|
||||||
|
stahp_node(n, "generic_visitor: unhandled %s", AST_KIND_STR[n->nodeKind]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(postHandler) postHandler(nptr, stmt, stmtPrev, chu, tlc, ud);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ast_expression_equal(AST *a, AST *b) {
|
||||||
|
if(!a && !b) return 1;
|
||||||
|
if(a == b) return 1;
|
||||||
|
|
||||||
|
if(a->nodeKind != b->nodeKind) return 0;
|
||||||
|
|
||||||
|
if(a->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
return a->exprPrim.val == b->exprPrim.val;
|
||||||
|
} else if(a->nodeKind == AST_EXPR_VAR) {
|
||||||
|
return a->exprVar.thing == b->exprVar.thing;
|
||||||
|
} else if(a->nodeKind == AST_EXPR_UNARY_OP) {
|
||||||
|
return a->exprUnOp.operator == b->exprUnOp.operator && ast_expression_equal(a->exprUnOp.operand, b->exprUnOp.operand);
|
||||||
|
} else if(a->nodeKind == AST_EXPR_BINARY_OP) {
|
||||||
|
return a->exprBinOp.operator == b->exprBinOp.operator && ast_expression_equal(a->exprBinOp.operands[0], b->exprBinOp.operands[0]) && ast_expression_equal(a->exprBinOp.operands[1], b->exprBinOp.operands[1]);
|
||||||
|
} else if(a->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
return 1;
|
||||||
|
} else if(a->nodeKind == AST_EXPR_CAST) {
|
||||||
|
return ast_expression_equal(a->exprCast.what, b->exprCast.what) && type_equal(a->exprCast.to, b->exprCast.to) && a->exprCast.reinterpretation == b->exprCast.reinterpretation;
|
||||||
|
} else {
|
||||||
|
stahp_node(a, "ast_expression_equal: unhandled %s", AST_KIND_STR[a->nodeKind]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function may return three values: YES (1), NO (0) or UNKNOWN (-1).
|
||||||
|
// ... Ew
|
||||||
|
int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2) {
|
||||||
|
const AST *s = chunk->chunk.statementFirst;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
if(s && s->nodeKind == AST_STMT_LOOP) {
|
||||||
|
int i = ast_stmt_is_after(s->stmtLoop.body, s1, s2);
|
||||||
|
if(i == 1 || (i == 0 && s1 != NULL)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s == s1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(s == s2) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!s) break;
|
||||||
|
|
||||||
|
if(s->nodeKind == AST_STMT_IF) {
|
||||||
|
int i = ast_stmt_is_after(s->stmtIf.then, s1, s2);
|
||||||
|
if(i == 1 || (i == 0 && s1 != NULL)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s->stmtIf.elss) {
|
||||||
|
i = ast_stmt_is_after(s->stmtIf.elss, s1, s2);
|
||||||
|
if(i == 1 || (i == 0 && s1 != NULL)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s = s->statement.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AST *ast_get_label_by_name(AST *tlc, const char *name) {
|
||||||
|
for(AST *s = tlc->chunk.statementFirst; s; s = s->statement.next) {
|
||||||
|
if(s->nodeKind == AST_STMT_LABEL && !strcmp(s->stmtLabel.name, name)) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stahp(0, 0, "Label %s not found", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *memdup(void *a, size_t len) {
|
||||||
|
void *r = malloc(len);
|
||||||
|
memcpy(r, a, len);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WARNING: Just because you deep copy an AST node, does not mean
|
||||||
|
* ast_expression_equal will return true! This matters for example with
|
||||||
|
* function calls (a function call is not equal to itself).
|
||||||
|
*/
|
||||||
|
AST *ast_deep_copy(AST *src) {
|
||||||
|
if(src->nodeKind == AST_EXPR_VAR) {
|
||||||
|
return memdup(src, sizeof(ASTExprVar));
|
||||||
|
} else if(src->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
return memdup(src, sizeof(ASTExprPrimitive));
|
||||||
|
} else if(src->nodeKind == AST_EXPR_VAR) {
|
||||||
|
return memdup(src, sizeof(ASTExprVar));
|
||||||
|
} else if(src->nodeKind == AST_EXPR_UNARY_OP) {
|
||||||
|
ASTExprUnaryOp *n = memdup(src, sizeof(ASTExprUnaryOp));
|
||||||
|
n->operand = ast_deep_copy(n->operand);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
AST *ast_cast_expr(AST *what, Type *to) {
|
||||||
|
if(what == NULL) {
|
||||||
|
stahp(0, 0, "NULL what passed to ast_cast_expr");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only exists at parse-time, hence not part of type system and is handled separately */
|
||||||
|
if(what->nodeKind == AST_EXPR_STRING_LITERAL) {
|
||||||
|
if(to->type == TYPE_TYPE_ARRAY && type_equal(primitive_parse("u8"), to->array.of) && (to->array.length == what->exprStrLit.length || to->array.length == -1)) {
|
||||||
|
ASTExprArray *ret = calloc(1, sizeof(*ret));
|
||||||
|
ret->nodeKind = AST_EXPR_ARRAY;
|
||||||
|
ret->items = calloc(what->exprStrLit.length, sizeof(*ret->items));
|
||||||
|
ret->type = to;
|
||||||
|
|
||||||
|
// If this declaration is of an unknown-length array type (eg u8[?]) then update the length
|
||||||
|
if(to->array.length == -1) {
|
||||||
|
to->array.length = what->exprStrLit.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < what->exprStrLit.length; i++) {
|
||||||
|
uint8_t bajt = what->exprStrLit.data[i];
|
||||||
|
|
||||||
|
ASTExprPrimitive *item = calloc(1, sizeof(*item));
|
||||||
|
item->nodeKind = AST_EXPR_PRIMITIVE;
|
||||||
|
item->type = to->array.of;
|
||||||
|
item->val = bajt;
|
||||||
|
|
||||||
|
ret->items[i] = (AST*) item;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (AST*) ret;
|
||||||
|
} else if(to->type == TYPE_TYPE_PRIMITIVE) {
|
||||||
|
if(to->primitive.width != what->exprStrLit.length * 8) {
|
||||||
|
stahp_node(what, "Size mismatch between string literal and target type");
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTExprPrimitive *ret = calloc(1, sizeof(*ret));
|
||||||
|
ret->nodeKind = AST_EXPR_PRIMITIVE;
|
||||||
|
ret->type = to;
|
||||||
|
ret->val = 0;
|
||||||
|
memcpy(&ret->val, what->exprStrLit.data, what->exprStrLit.length);
|
||||||
|
return (AST*) ret;
|
||||||
|
} else abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool copy = false;
|
||||||
|
|
||||||
|
// Make sure an unparametrized generic int parameter hasn't sneaked its way in
|
||||||
|
while(what->nodeKind == AST_EXPR_VAR && what->exprVar.thing->kind == SCOPEITEM_CEXPR && what->exprVar.thing->data.cexpr.concrete) {
|
||||||
|
what = what->exprVar.thing->data.cexpr.concrete;
|
||||||
|
copy = true;
|
||||||
|
}
|
||||||
|
assert(!(what->nodeKind == AST_EXPR_VAR && what->exprVar.thing->kind == SCOPEITEM_CEXPR));
|
||||||
|
|
||||||
|
if(copy) {
|
||||||
|
what = ast_deep_copy(what);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type_equal(what->expression.type, to)) return what;
|
||||||
|
|
||||||
|
if(!type_is_castable(what->expression.type, to)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(what->nodeKind == AST_EXPR_PRIMITIVE && (to->type == TYPE_TYPE_PRIMITIVE || to->type == TYPE_TYPE_POINTER)) {
|
||||||
|
ASTExprPrimitive *ret = calloc(1, sizeof(*ret));
|
||||||
|
ret->nodeKind = AST_EXPR_PRIMITIVE;
|
||||||
|
ret->type = to;
|
||||||
|
|
||||||
|
if(to->type == TYPE_TYPE_PRIMITIVE) {
|
||||||
|
ret->val = what->exprPrim.val & ((1UL << to->primitive.width) - 1);
|
||||||
|
} else {
|
||||||
|
ret->val = what->exprPrim.val & ((1UL << (8 * type_size(to))) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (AST*) ret;
|
||||||
|
} else {
|
||||||
|
ASTExprCast *ret = calloc(1, sizeof(*ret));
|
||||||
|
ret->nodeKind = AST_EXPR_CAST;
|
||||||
|
ret->type = to;
|
||||||
|
ret->what = what;
|
||||||
|
ret->to = to;
|
||||||
|
return (AST*) ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
fail:
|
||||||
|
stahp_node(what, "Cannot cast type %s into %s", type_to_string(what->expression.type), type_to_string(to));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ReferencesStackState {
|
||||||
|
bool yes;
|
||||||
|
};
|
||||||
|
static void references_stack_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
AST *n = *nptr;
|
||||||
|
if(n->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
((struct ReferencesStackState*) ud)->yes = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ast_references_stack(AST *a) {
|
||||||
|
struct ReferencesStackState state = {};
|
||||||
|
generic_visitor(&a, NULL, NULL, NULL, NULL, &state, references_stack_visitor, NULL);
|
||||||
|
return state.yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IsScopeItemReferenced {
|
||||||
|
bool yes;
|
||||||
|
ScopeItem *si;
|
||||||
|
};
|
||||||
|
static void is_scopeitem_referenced_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
struct IsScopeItemReferenced *state = ud;
|
||||||
|
|
||||||
|
AST *n = *aptr;
|
||||||
|
if(n->nodeKind == AST_EXPR_UNARY_OP) {
|
||||||
|
if(n->exprUnOp.operand->nodeKind == AST_EXPR_VAR && n->exprUnOp.operator == UNOP_REF && n->exprUnOp.operand->exprVar.thing == state->si) {
|
||||||
|
state->yes = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool ast_is_scopeitem_referenced(AST *tlc, ScopeItem *si) {
|
||||||
|
struct IsScopeItemReferenced state = {.si = si};
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, NULL, is_scopeitem_referenced_visitor);
|
||||||
|
return state.yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeItem *ast_tlc_new_var(AST *tlc, char *name, Type *itstype) {
|
||||||
|
ScopeItem *vte = calloc(1, sizeof(*vte));
|
||||||
|
vte->kind = SCOPEITEM_VAR;
|
||||||
|
vte->type = itstype;
|
||||||
|
vte->data.var.color = -1;
|
||||||
|
vte->data.var.precolored = false;
|
||||||
|
vte->data.var.degree = 0;
|
||||||
|
vte->data.var.priority = 0;
|
||||||
|
vte->data.var.name = name;
|
||||||
|
|
||||||
|
// Add to var array
|
||||||
|
tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (++tlc->chunk.varCount));
|
||||||
|
tlc->chunk.vars[tlc->chunk.varCount - 1] = vte;
|
||||||
|
|
||||||
|
return vte;
|
||||||
|
}
|
||||||
411
src/ast/ast.h
Normal file
411
src/ast/ast.h
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
#ifndef NCTREF_AST_H
|
||||||
|
#define NCTREF_AST_H
|
||||||
|
|
||||||
|
#include"types.h"
|
||||||
|
#include"lexer.h"
|
||||||
|
#include"scope.h"
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define ENUMPAK __attribute__((packed))
|
||||||
|
#else
|
||||||
|
#define ENUMPAK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GEN_ENUM(x) x,
|
||||||
|
#define GEN_STRI(x) #x,
|
||||||
|
|
||||||
|
#define AST_KINDS(K) \
|
||||||
|
K(AST_MISSING) \
|
||||||
|
K(AST_CHUNK) \
|
||||||
|
K(AST_STMT_DECL) \
|
||||||
|
K(AST_EXPR_PRIMITIVE) \
|
||||||
|
K(AST_STMT_IF) \
|
||||||
|
K(AST_EXPR_BINARY_OP) \
|
||||||
|
K(AST_EXPR_VAR) \
|
||||||
|
K(AST_EXPR_STACK_POINTER) \
|
||||||
|
K(AST_EXPR_UNARY_OP) \
|
||||||
|
K(AST_STMT_LOOP) \
|
||||||
|
K(AST_STMT_BREAK) \
|
||||||
|
K(AST_STMT_CONTINUE) \
|
||||||
|
K(AST_EXPR_CALL) \
|
||||||
|
K(AST_STMT_EXPR) \
|
||||||
|
K(AST_STMT_ASSIGN) \
|
||||||
|
K(AST_STMT_EXT_ALIGN) \
|
||||||
|
K(AST_EXPR_STRING_LITERAL) \
|
||||||
|
K(AST_EXPR_CAST) \
|
||||||
|
K(AST_EXPR_ARRAY) \
|
||||||
|
K(AST_EXPR_FUNC) \
|
||||||
|
K(AST_STMT_EXT_ORG) \
|
||||||
|
K(AST_SECTION) \
|
||||||
|
K(AST_STMT_RETURN) \
|
||||||
|
K(AST_EXPR_EXT_SALLOC) \
|
||||||
|
K(AST_EXPR_DOT) \
|
||||||
|
K(AST_EXPR_EXT_SIZEOF) \
|
||||||
|
K(AST_STMT_JUMP) \
|
||||||
|
K(AST_STMT_LABEL) \
|
||||||
|
K(AST_EXPR_NULL)
|
||||||
|
|
||||||
|
typedef enum ENUMPAK { AST_KINDS(GEN_ENUM) } ASTKind;
|
||||||
|
extern const char *AST_KIND_STR[];
|
||||||
|
|
||||||
|
typedef enum ENUMPAK {
|
||||||
|
BINOP_ADD = 0,
|
||||||
|
BINOP_SUB = 1,
|
||||||
|
BINOP_BITWISE_AND = 2,
|
||||||
|
BINOP_BITWISE_OR = 3,
|
||||||
|
BINOP_BITWISE_XOR = 4,
|
||||||
|
|
||||||
|
BINOP_SIMPLES = 5,
|
||||||
|
|
||||||
|
BINOP_MUL = 5,
|
||||||
|
BINOP_DIV = 6,
|
||||||
|
|
||||||
|
BINOP_2OPS = 7,
|
||||||
|
|
||||||
|
BINOP_MULHI = 7,
|
||||||
|
BINOP_MOD = 8,
|
||||||
|
|
||||||
|
BINOP_EQUAL = 9,
|
||||||
|
BINOP_NEQUAL = 10,
|
||||||
|
BINOP_LESS = 11,
|
||||||
|
BINOP_GREATER = 12,
|
||||||
|
BINOP_LEQUAL = 13,
|
||||||
|
BINOP_GEQUAL = 14,
|
||||||
|
|
||||||
|
BINOP_LOGICAL_AND = 15,
|
||||||
|
BINOP_LOGICAL_OR = 16,
|
||||||
|
|
||||||
|
BINOP_DEREF = 17,
|
||||||
|
|
||||||
|
BINOP_WTF = 999,
|
||||||
|
} BinaryOp;
|
||||||
|
|
||||||
|
static inline int binop_is_comparison(BinaryOp op) {
|
||||||
|
return op == BINOP_EQUAL || op == BINOP_NEQUAL || op == BINOP_LESS || op == BINOP_GREATER || op == BINOP_LEQUAL || op == BINOP_GEQUAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int binop_is_commutative(BinaryOp op) {
|
||||||
|
return op == BINOP_ADD || op == BINOP_MUL || op == BINOP_MULHI || op == BINOP_EQUAL || op == BINOP_NEQUAL || op == BINOP_BITWISE_AND || op == BINOP_BITWISE_OR || op == BINOP_BITWISE_XOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline BinaryOp binop_comp_opposite(BinaryOp op) {
|
||||||
|
if(op == BINOP_EQUAL) {
|
||||||
|
return BINOP_NEQUAL;
|
||||||
|
} else if(op == BINOP_NEQUAL) {
|
||||||
|
return BINOP_EQUAL;
|
||||||
|
} else if(op == BINOP_LESS) {
|
||||||
|
return BINOP_GEQUAL;
|
||||||
|
} else if(op == BINOP_GREATER) {
|
||||||
|
return BINOP_LEQUAL;
|
||||||
|
} else if(op == BINOP_LEQUAL) {
|
||||||
|
return BINOP_GREATER;
|
||||||
|
} else if(op == BINOP_GEQUAL) {
|
||||||
|
return BINOP_LESS;
|
||||||
|
}
|
||||||
|
return BINOP_WTF;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum ENUMPAK {
|
||||||
|
UNOP_NEGATE = 1,
|
||||||
|
UNOP_BITWISE_NOT = 2,
|
||||||
|
UNOP_REF = 3,
|
||||||
|
UNOP_NOT = 4,
|
||||||
|
} UnaryOp;
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTKind nodeKind;
|
||||||
|
uint16_t row;
|
||||||
|
uint16_t col;
|
||||||
|
} ASTBase;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTBase;
|
||||||
|
Type *type;
|
||||||
|
} ASTExpr;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
int val;
|
||||||
|
|
||||||
|
// If true, increase this literal during stack growths
|
||||||
|
bool stackGrowth;
|
||||||
|
} ASTExprPrimitive;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
union AST *operands[2];
|
||||||
|
BinaryOp operator;
|
||||||
|
} ASTExprBinaryOp;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
UnaryOp operator;
|
||||||
|
union AST *operand;
|
||||||
|
} ASTExprUnaryOp;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
ScopeItem *thing;
|
||||||
|
} ASTExprVar;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
union AST *what;
|
||||||
|
|
||||||
|
union AST **args;
|
||||||
|
} ASTExprCall;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
size_t length;
|
||||||
|
char *data;
|
||||||
|
} ASTExprStringLiteral;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
} ASTExprStackPointer;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
union AST *chunk;
|
||||||
|
|
||||||
|
// Necessary for when the parser jumps to a generic function
|
||||||
|
Scope *scope;
|
||||||
|
Token *rangeTokens;
|
||||||
|
size_t startTokI;
|
||||||
|
size_t endTokI;
|
||||||
|
} ASTExprFunc;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTBase;
|
||||||
|
union AST *next;
|
||||||
|
|
||||||
|
// Used for reaching definition analysis
|
||||||
|
bool dirty;
|
||||||
|
ReachingDefs rd;
|
||||||
|
} ASTStmt;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
|
||||||
|
ScopeItem *thing;
|
||||||
|
|
||||||
|
union AST *expression;
|
||||||
|
} ASTStmtDecl;
|
||||||
|
|
||||||
|
struct ASTChunk;
|
||||||
|
typedef struct ASTSection {
|
||||||
|
ASTBase;
|
||||||
|
|
||||||
|
Token name;
|
||||||
|
struct ASTChunk *tlc;
|
||||||
|
|
||||||
|
struct ASTSection *next;
|
||||||
|
} ASTSection;
|
||||||
|
|
||||||
|
typedef struct ASTChunk {
|
||||||
|
ASTBase;
|
||||||
|
|
||||||
|
/* Flattened variable array for global register allocation */
|
||||||
|
size_t varCount;
|
||||||
|
ScopeItem **vars;
|
||||||
|
|
||||||
|
/* extern symbol array */
|
||||||
|
size_t externCount;
|
||||||
|
ScopeItem **externs;
|
||||||
|
|
||||||
|
union AST *statementFirst;
|
||||||
|
union AST *statementLast;
|
||||||
|
|
||||||
|
size_t stackReservation;
|
||||||
|
|
||||||
|
/* NULL unless this is a top-level chunk belonging to a function */
|
||||||
|
Type *functionType;
|
||||||
|
} ASTChunk;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
|
||||||
|
union AST *expression;
|
||||||
|
|
||||||
|
union AST *then;
|
||||||
|
union AST *elss;
|
||||||
|
} ASTStmtIf;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
|
||||||
|
union AST *body;
|
||||||
|
} ASTStmtLoop;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
} ASTStmtBreak;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
} ASTStmtContinue;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
|
||||||
|
union AST *expr;
|
||||||
|
} ASTStmtExpr;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
|
||||||
|
union AST *what;
|
||||||
|
union AST *to;
|
||||||
|
} ASTStmtAssign;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
|
||||||
|
int val;
|
||||||
|
} ASTStmtExtAlign;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
union AST *what;
|
||||||
|
Type *to;
|
||||||
|
|
||||||
|
char reinterpretation; /* 1 = as, 0 = to */
|
||||||
|
} ASTExprCast;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
union AST **items;
|
||||||
|
} ASTExprArray;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
Type *size;
|
||||||
|
} ASTExprExtSalloc;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
union AST *a;
|
||||||
|
const char *b;
|
||||||
|
} ASTExprDot;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
|
||||||
|
size_t val;
|
||||||
|
} ASTStmtExtOrg;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
|
||||||
|
union AST *val;
|
||||||
|
} ASTStmtReturn;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
|
||||||
|
// One of these will be NULL
|
||||||
|
union AST *ofExpr;
|
||||||
|
Type *ofType;
|
||||||
|
} ASTExprExtSizeOf;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
|
||||||
|
union AST *condition;
|
||||||
|
char *label;
|
||||||
|
} ASTStmtJump;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTStmt;
|
||||||
|
|
||||||
|
char *name;
|
||||||
|
} ASTStmtLabel;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ASTExpr;
|
||||||
|
} ASTExprNull;
|
||||||
|
|
||||||
|
typedef union AST {
|
||||||
|
ASTBase;
|
||||||
|
|
||||||
|
ASTChunk chunk;
|
||||||
|
ASTStmt statement;
|
||||||
|
ASTStmtDecl stmtDecl;
|
||||||
|
ASTStmtIf stmtIf;
|
||||||
|
ASTStmtLoop stmtLoop;
|
||||||
|
ASTStmtBreak stmtBreak;
|
||||||
|
ASTStmtContinue stmtContinue;
|
||||||
|
ASTStmtExpr stmtExpr;
|
||||||
|
ASTStmtAssign stmtAssign;
|
||||||
|
ASTStmtReturn stmtReturn;
|
||||||
|
ASTExpr expression;
|
||||||
|
ASTExprPrimitive exprPrim;
|
||||||
|
ASTExprBinaryOp exprBinOp;
|
||||||
|
ASTExprUnaryOp exprUnOp;
|
||||||
|
ASTExprVar exprVar;
|
||||||
|
ASTExprCall exprCall;
|
||||||
|
ASTStmtExtAlign stmtExtAlign;
|
||||||
|
ASTExprStringLiteral exprStrLit;
|
||||||
|
ASTExprCast exprCast;
|
||||||
|
ASTExprArray exprArray;
|
||||||
|
ASTExprFunc exprFunc;
|
||||||
|
ASTExprDot exprDot;
|
||||||
|
ASTExprExtSalloc exprExtSalloc;
|
||||||
|
ASTStmtExtOrg stmtExtOrg;
|
||||||
|
ASTSection section;
|
||||||
|
ASTExprExtSizeOf exprExtSizeOf;
|
||||||
|
ASTStmtJump stmtJump;
|
||||||
|
ASTStmtLabel stmtLabel;
|
||||||
|
ASTExprNull exprNull;
|
||||||
|
} AST;
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
typedef void(*GenericVisitorHandler)(AST**, AST*, AST*, AST*, AST*, void*);
|
||||||
|
void generic_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, void *ud, GenericVisitorHandler preHandler, GenericVisitorHandler postHandler);
|
||||||
|
|
||||||
|
int ast_expression_equal(AST*, AST*);
|
||||||
|
|
||||||
|
int ast_stmt_is_after(const AST *chunk, const AST *s1, const AST *s2);
|
||||||
|
|
||||||
|
AST *ast_deep_copy(AST*);
|
||||||
|
|
||||||
|
AST *ast_cast_expr(AST *what, Type *to);
|
||||||
|
|
||||||
|
void ast_typecheck(AST *tlc);
|
||||||
|
|
||||||
|
AST *ast_get_label_by_name(AST *tlc, const char *name);
|
||||||
|
|
||||||
|
bool ast_references_stack(AST *a);
|
||||||
|
|
||||||
|
bool ast_is_scopeitem_referenced(AST *tlc, ScopeItem *si);
|
||||||
|
|
||||||
|
// name becomes OWNED
|
||||||
|
ScopeItem *ast_tlc_new_var(AST *tlc, char *name, Type *itstype);
|
||||||
|
|
||||||
|
#include"dump.h"
|
||||||
|
#include"stack.h"
|
||||||
|
#include"desegment.h"
|
||||||
|
#include"sroa.h"
|
||||||
|
#include"linearize.h"
|
||||||
|
#include"usedef.h"
|
||||||
|
#include"commutativity.h"
|
||||||
|
#include"scr.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
21
src/ast/commutativity.c
Normal file
21
src/ast/commutativity.c
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include"commutativity.h"
|
||||||
|
|
||||||
|
#include"ast.h"
|
||||||
|
|
||||||
|
static void commutativity_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
AST *n = *nptr;
|
||||||
|
|
||||||
|
if(n->nodeKind == AST_EXPR_BINARY_OP) {
|
||||||
|
if(binop_is_commutative(n->exprBinOp.operator)) {
|
||||||
|
if(n->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[1]->nodeKind != AST_EXPR_PRIMITIVE) {
|
||||||
|
AST *tmp = n->exprBinOp.operands[0];
|
||||||
|
n->exprBinOp.operands[0] = n->exprBinOp.operands[1];
|
||||||
|
n->exprBinOp.operands[1] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ast_commutativity_pass(AST *tlc) {
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, NULL, NULL, commutativity_visitor);
|
||||||
|
}
|
||||||
|
|
||||||
8
src/ast/commutativity.h
Normal file
8
src/ast/commutativity.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
|
||||||
|
// This pass makes sure that integer literals are always operand 1
|
||||||
|
// (the right operand) in a commutative binary operation,
|
||||||
|
// simplifying passes further down.
|
||||||
|
void ast_commutativity_pass(union AST *chu);
|
||||||
93
src/ast/desegment.c
Normal file
93
src/ast/desegment.c
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#include"desegment.h"
|
||||||
|
|
||||||
|
#include"ast.h"
|
||||||
|
#include"ntc.h"
|
||||||
|
|
||||||
|
#include"x86/arch.h"
|
||||||
|
|
||||||
|
#include"utils.h"
|
||||||
|
#include<stdlib.h>
|
||||||
|
|
||||||
|
static void ast_tlc_add_var(AST *tlc, ScopeItem *si) {
|
||||||
|
tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * ++tlc->chunk.varCount);
|
||||||
|
tlc->chunk.vars[tlc->chunk.varCount - 1] = si;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Split away complex expression into a new local variable */
|
||||||
|
static AST *varify(AST *tlc, AST *chunk, AST **stmtPrev, AST *stmt, AST *e) {
|
||||||
|
static size_t idx = 0;
|
||||||
|
|
||||||
|
ScopeItem *vte = calloc(1, sizeof(*vte));
|
||||||
|
vte->kind = SCOPEITEM_VAR;
|
||||||
|
vte->type = e->expression.type;
|
||||||
|
vte->data.var.name = malp("$varify_%lu", idx++);
|
||||||
|
|
||||||
|
ast_tlc_add_var(tlc, vte);
|
||||||
|
|
||||||
|
// Alter AST
|
||||||
|
|
||||||
|
ASTExprVar *ev[2];
|
||||||
|
for(int i = 0; i < 2; i++) {
|
||||||
|
ev[i] = calloc(1, sizeof(ASTExprVar));
|
||||||
|
ev[i]->nodeKind = AST_EXPR_VAR;
|
||||||
|
ev[i]->type = e->expression.type;
|
||||||
|
ev[i]->thing = vte;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTStmtAssign *assign = calloc(1, sizeof(*assign));
|
||||||
|
assign->nodeKind = AST_STMT_ASSIGN;
|
||||||
|
assign->what = (AST*) ev[0];
|
||||||
|
assign->to = e;
|
||||||
|
vte->data.var.declaration = (AST*) assign;
|
||||||
|
|
||||||
|
if(*stmtPrev) {
|
||||||
|
(*stmtPrev)->statement.next = (AST*) assign;
|
||||||
|
} else {
|
||||||
|
chunk->chunk.statementFirst = (AST*) assign;
|
||||||
|
}
|
||||||
|
assign->next = stmt;
|
||||||
|
*stmtPrev = (AST*) assign;
|
||||||
|
|
||||||
|
// Reset ud-chain
|
||||||
|
|
||||||
|
vte->data.var.usedefFirst = NULL;
|
||||||
|
vte->data.var.usedefLast = NULL;
|
||||||
|
|
||||||
|
return (AST*) ev[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ast_segmented_dereference_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
if(tlc != ud) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AST *n = *aptr;
|
||||||
|
if(n->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operator == BINOP_DEREF && type_is_segmented_pointer(n->exprBinOp.operands[0]->expression.type)) {
|
||||||
|
static size_t idx = 0;
|
||||||
|
|
||||||
|
AST *v;
|
||||||
|
if(n->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR)
|
||||||
|
v = n->exprBinOp.operands[0];
|
||||||
|
else
|
||||||
|
v = varify(tlc, chunk, &stmtPrev, stmt, n->exprBinOp.operands[0]);
|
||||||
|
|
||||||
|
ASTExprDot *edseg = calloc(1, sizeof(*edseg));
|
||||||
|
edseg->type = n->exprBinOp.operands[0]->expression.type->record.fieldTypes[0];
|
||||||
|
edseg->nodeKind = AST_EXPR_DOT;
|
||||||
|
edseg->a = v;
|
||||||
|
edseg->b = strdup("segment");
|
||||||
|
|
||||||
|
ASTExprDot *ed = calloc(1, sizeof(*ed));
|
||||||
|
ed->type = n->exprBinOp.operands[0]->expression.type->record.fieldTypes[1];
|
||||||
|
ed->nodeKind = AST_EXPR_DOT;
|
||||||
|
ed->a = ast_deep_copy(v);
|
||||||
|
ed->b = strdup("offset");
|
||||||
|
|
||||||
|
n->exprBinOp.operands[0] = (AST*) ed;
|
||||||
|
n->exprBinOp.operands[1] = (AST*) edseg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ast_segmented_dereference(AST *tlc) {
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, ast_segmented_dereference_visitor, NULL);
|
||||||
|
}
|
||||||
7
src/ast/desegment.h
Normal file
7
src/ast/desegment.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
|
||||||
|
// Convert segmented derefences like *x into segment move and *x.offset.
|
||||||
|
// Must be done before ast_sroa.
|
||||||
|
void ast_segmented_dereference(union AST *tlc);
|
||||||
348
src/ast/dump.c
Normal file
348
src/ast/dump.c
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
#include"dump.h"
|
||||||
|
|
||||||
|
#include"ast.h"
|
||||||
|
#include"types.h"
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
#include<string.h>
|
||||||
|
#include"utils.h"
|
||||||
|
|
||||||
|
char *type_to_string(Type *t) {
|
||||||
|
if(t->type == TYPE_TYPE_PRIMITIVE) {
|
||||||
|
char ret[16] = {};
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
ret[i++] = t->primitive.isFloat ? 'f' : (t->primitive.isUnsigned ? 'u' : 'i');
|
||||||
|
snprintf(ret + i, sizeof(ret) - i, "%i", t->primitive.width);
|
||||||
|
|
||||||
|
return strdup(ret);
|
||||||
|
} else if(t->type == TYPE_TYPE_POINTER) {
|
||||||
|
char *c = type_to_string(t->pointer.of);
|
||||||
|
char *r = malp("%s*", c);
|
||||||
|
free(c);
|
||||||
|
return r;
|
||||||
|
} else if(t->type == TYPE_TYPE_RECORD) {
|
||||||
|
return malp("%s", t->record.name);
|
||||||
|
} else if(t->type == TYPE_TYPE_GENERIC) {
|
||||||
|
return malp("%s", t->generic.paramName);
|
||||||
|
} else if(t->type == TYPE_TYPE_ARRAY) {
|
||||||
|
char *of = type_to_string(t->array.of);
|
||||||
|
char *len = NULL;
|
||||||
|
if(t->array.lengthIsGeneric) {
|
||||||
|
len = malp("");
|
||||||
|
} else {
|
||||||
|
len = malp("%li", t->array.length);
|
||||||
|
}
|
||||||
|
char *r = malp("%s[%s]", of, len);
|
||||||
|
free(of);
|
||||||
|
free(len);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strdup("@unimp");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *ast_dumpe(AST *tlc, AST *e) {
|
||||||
|
if(!e) {
|
||||||
|
return malp("(null)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
return malp("%i", e->exprPrim.val);
|
||||||
|
} else if(e->nodeKind == AST_EXPR_VAR) {
|
||||||
|
ScopeItem *vte = e->exprVar.thing;
|
||||||
|
|
||||||
|
if(vte->kind == SCOPEITEM_VAR) {
|
||||||
|
return strdup(vte->data.var.name);
|
||||||
|
} else if(vte->kind == SCOPEITEM_SYMBOL) {
|
||||||
|
return strdup(vte->data.symbol.name);
|
||||||
|
} else if(vte->kind == SCOPEITEM_CEXPR) {
|
||||||
|
return ast_dumpe(tlc, vte->data.cexpr.concrete);
|
||||||
|
} else abort();
|
||||||
|
} else if(e->nodeKind == AST_EXPR_UNARY_OP) {
|
||||||
|
const char *op = NULL;
|
||||||
|
switch(e->exprUnOp.operator) {
|
||||||
|
case UNOP_REF:
|
||||||
|
op = "&";
|
||||||
|
break;
|
||||||
|
case UNOP_BITWISE_NOT:
|
||||||
|
op = "~";
|
||||||
|
break;
|
||||||
|
case UNOP_NEGATE:
|
||||||
|
op = "-";
|
||||||
|
break;
|
||||||
|
case UNOP_NOT:
|
||||||
|
op = "!";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
char *c = ast_dumpe(tlc, e->exprUnOp.operand);
|
||||||
|
char *r = malp("%s%s", op, c);
|
||||||
|
free(c);
|
||||||
|
return r;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_DEREF) {
|
||||||
|
char *a = ast_dumpe(tlc, e->exprBinOp.operands[0]);
|
||||||
|
char *r;
|
||||||
|
if(e->exprBinOp.operands[1]) {
|
||||||
|
char *b = ast_dumpe(tlc, e->exprBinOp.operands[1]);
|
||||||
|
r = malp("*[%s]%s", b, a);
|
||||||
|
free(b);
|
||||||
|
} else {
|
||||||
|
r = malp("*%s", a);
|
||||||
|
}
|
||||||
|
free(a);
|
||||||
|
return r;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_BINARY_OP) {
|
||||||
|
char *a = ast_dumpe(tlc, e->exprBinOp.operands[0]);
|
||||||
|
char *b = ast_dumpe(tlc, e->exprBinOp.operands[1]);
|
||||||
|
const char *op;
|
||||||
|
switch(e->exprBinOp.operator) {
|
||||||
|
case BINOP_ADD:
|
||||||
|
op = "+";
|
||||||
|
break;
|
||||||
|
case BINOP_SUB:
|
||||||
|
op = "-";
|
||||||
|
break;
|
||||||
|
case BINOP_MUL:
|
||||||
|
op = "*";
|
||||||
|
break;
|
||||||
|
case BINOP_DIV:
|
||||||
|
op = "/";
|
||||||
|
break;
|
||||||
|
case BINOP_BITWISE_AND:
|
||||||
|
op = "&";
|
||||||
|
break;
|
||||||
|
case BINOP_BITWISE_OR:
|
||||||
|
op = "|";
|
||||||
|
break;
|
||||||
|
case BINOP_BITWISE_XOR:
|
||||||
|
op = "^";
|
||||||
|
break;
|
||||||
|
case BINOP_EQUAL:
|
||||||
|
op = "==";
|
||||||
|
break;
|
||||||
|
case BINOP_NEQUAL:
|
||||||
|
op = "!=";
|
||||||
|
break;
|
||||||
|
case BINOP_LESS:
|
||||||
|
op = "<";
|
||||||
|
break;
|
||||||
|
case BINOP_GREATER:
|
||||||
|
op = ">";
|
||||||
|
break;
|
||||||
|
case BINOP_LEQUAL:
|
||||||
|
op = "<=";
|
||||||
|
break;
|
||||||
|
case BINOP_GEQUAL:
|
||||||
|
op = ">=";
|
||||||
|
break;
|
||||||
|
case BINOP_MULHI:
|
||||||
|
op = "*^";
|
||||||
|
break;
|
||||||
|
case BINOP_LOGICAL_AND:
|
||||||
|
op = "&&";
|
||||||
|
break;
|
||||||
|
case BINOP_LOGICAL_OR:
|
||||||
|
op = "||";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
char *r = malp("(%s %s %s)", a, op, b);
|
||||||
|
free(a);
|
||||||
|
free(b);
|
||||||
|
return r;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
return malp("@stack");
|
||||||
|
} else if(e->nodeKind == AST_EXPR_FUNC) {
|
||||||
|
char *out = NULL;
|
||||||
|
|
||||||
|
if(type_is_generic(e->expression.type)) {
|
||||||
|
out = malp("(generic)");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
char *rettype = type_to_string(e->expression.type->function.ret);
|
||||||
|
out = malp("%s(", rettype);
|
||||||
|
free(rettype);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < e->expression.type->function.argCount; i++) {
|
||||||
|
char *argtype = type_to_string(e->expression.type->function.args[i]);
|
||||||
|
char *out2 = malp(i == e->expression.type->function.argCount - 1 ? "%s%s" : "%s%s, ", out, argtype);
|
||||||
|
free(out);
|
||||||
|
free(argtype);
|
||||||
|
out = out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
char *choonk = ast_dumpc(tlc, e->exprFunc.chunk);
|
||||||
|
char *out2 = malp("%s) {\n%s}", out, choonk);
|
||||||
|
free(out);
|
||||||
|
free(choonk);
|
||||||
|
out = out2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_CALL) {
|
||||||
|
char *w = ast_dumpe(tlc, e->exprCall.what);
|
||||||
|
char *out = malp("%s(", w);
|
||||||
|
free(w);
|
||||||
|
size_t argCount = e->exprCall.what->expression.type->pointer.of->function.argCount;
|
||||||
|
for(size_t i = 0; i < argCount; i++) {
|
||||||
|
char *a = ast_dumpe(tlc, e->exprCall.args[i]);
|
||||||
|
char *out2 = malp(i == argCount - 1 ? "%s%s)" : "%s%s, ", out, a);
|
||||||
|
free(a);
|
||||||
|
free(out);
|
||||||
|
out = out2;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_EXT_SALLOC) {
|
||||||
|
char *w = type_to_string(e->exprExtSalloc.size);
|
||||||
|
char *out = malp("@salloc(%s)", w);
|
||||||
|
free(w);
|
||||||
|
return out;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_CAST) {
|
||||||
|
char *a = ast_dumpe(tlc, e->exprCast.what);
|
||||||
|
char *b = type_to_string(e->exprCast.to);
|
||||||
|
char *out = malp("(%s as %s)", a, b);
|
||||||
|
free(a);
|
||||||
|
free(b);
|
||||||
|
return out;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_DOT) {
|
||||||
|
char *a = ast_dumpe(tlc, e->exprDot.a);
|
||||||
|
char *out = malp(e->nodeKind == AST_EXPR_BINARY_OP ? "(%s).%s" : "%s.%s", a, e->exprDot.b);
|
||||||
|
free(a);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
return malp("@unimp:%s", AST_KIND_STR[e->nodeKind]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *ast_dumps(AST *tlc, AST *s) {
|
||||||
|
if(s->nodeKind == AST_STMT_ASSIGN) {
|
||||||
|
if(s->stmtAssign.to) {
|
||||||
|
char *a = ast_dumpe(tlc, s->stmtAssign.what);
|
||||||
|
char *b = ast_dumpe(tlc, s->stmtAssign.to);
|
||||||
|
char *r = malp("%s = %s;", a, b);
|
||||||
|
free(a);
|
||||||
|
free(b);
|
||||||
|
return r;
|
||||||
|
} else {
|
||||||
|
char *a = ast_dumpe(tlc, s->stmtAssign.what);
|
||||||
|
char *r = malp("%s = ; /* fake def */", a);
|
||||||
|
free(a);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} else if(s->nodeKind == AST_STMT_JUMP) {
|
||||||
|
char *a = ast_dumpe(tlc, s->stmtJump.condition);
|
||||||
|
char *r = malp("jump %s if %s;", s->stmtJump.label, a);
|
||||||
|
free(a);
|
||||||
|
return r;
|
||||||
|
} else if(s->nodeKind == AST_STMT_LABEL) {
|
||||||
|
return malp("@label %s;", s->stmtLabel.name);
|
||||||
|
} else if(s->nodeKind == AST_STMT_LOOP) {
|
||||||
|
char *inner = ast_dumpc(tlc, s->stmtLoop.body);
|
||||||
|
char *c = malp("loop {\n%s}", inner);
|
||||||
|
free(inner);
|
||||||
|
return c;
|
||||||
|
} else if(s->nodeKind == AST_STMT_IF) {
|
||||||
|
char *cond = ast_dumpe(tlc, s->stmtIf.expression);
|
||||||
|
char *inner = ast_dumpc(tlc, s->stmtIf.then);
|
||||||
|
char *elss = s->stmtIf.elss ? ast_dumpc(tlc, s->stmtIf.elss) : NULL;
|
||||||
|
char *c;
|
||||||
|
if(elss) {
|
||||||
|
c = malp("if(%s) {\n%s} else {\n%s}", cond, inner, elss);
|
||||||
|
free(elss);
|
||||||
|
} else {
|
||||||
|
c = malp("if(%s) {\n%s}", cond, inner);
|
||||||
|
}
|
||||||
|
free(cond);
|
||||||
|
free(inner);
|
||||||
|
return c;
|
||||||
|
} else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_VAR) {
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if(s->stmtExpr.expr->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||||
|
name = s->stmtExpr.expr->exprVar.thing->data.var.name;
|
||||||
|
} else {
|
||||||
|
name = s->stmtExpr.expr->exprVar.thing->data.symbol.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return malp("%s; /* loop guard */", name);
|
||||||
|
} else if(s->nodeKind == AST_STMT_EXPR) {
|
||||||
|
return ast_dumpe(tlc, s->stmtExpr.expr);
|
||||||
|
} else if(s->nodeKind == AST_STMT_DECL) {
|
||||||
|
ScopeItem *si = s->stmtDecl.thing;
|
||||||
|
const char *name = si->kind == SCOPEITEM_VAR ? si->data.var.name : si->data.symbol.name;
|
||||||
|
|
||||||
|
char *a = type_to_string(si->type);
|
||||||
|
char *c;
|
||||||
|
if(s->stmtDecl.expression) {
|
||||||
|
char *b = ast_dumpe(tlc, s->stmtDecl.expression);
|
||||||
|
c = malp("%s %s = %s;", a, name, b);
|
||||||
|
free(b);
|
||||||
|
} else {
|
||||||
|
c = malp("%s %s;", a, name);
|
||||||
|
}
|
||||||
|
free(a);
|
||||||
|
return c;
|
||||||
|
} else if(s->nodeKind == AST_STMT_RETURN) {
|
||||||
|
if(s->stmtReturn.val) {
|
||||||
|
char *e = ast_dumpe(tlc, s->stmtReturn.val);
|
||||||
|
char *c = malp("return %s;", e);
|
||||||
|
free(e);
|
||||||
|
return c;
|
||||||
|
} else {
|
||||||
|
return malp("return;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return malp("@unimp:%s;", AST_KIND_STR[s->nodeKind]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *stmt_live_range_dbgs(AST *tlc, AST *stmt) {
|
||||||
|
char *ret = malp("");
|
||||||
|
for(size_t v = 0; v < tlc->chunk.varCount; v++) {
|
||||||
|
ScopeItem *si = tlc->chunk.vars[v];
|
||||||
|
|
||||||
|
if(si->data.var.liveRangeStart == stmt) {
|
||||||
|
char *ret2 = malp("%s (%s start)", ret, si->data.var.name);
|
||||||
|
free(ret);
|
||||||
|
ret = ret2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(si->data.var.liveRangeEnd == stmt) {
|
||||||
|
char *ret2 = malp("%s (%s end)", ret, si->data.var.name);
|
||||||
|
free(ret);
|
||||||
|
ret = ret2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ast_dumpc(AST *tlc, AST *chu) {
|
||||||
|
AST *stmt = chu->chunk.statementFirst;
|
||||||
|
|
||||||
|
char *ret = malp("");
|
||||||
|
|
||||||
|
while(stmt) {
|
||||||
|
char *new = ast_dumps(tlc, stmt);
|
||||||
|
char *users = stmt_live_range_dbgs(tlc, stmt);
|
||||||
|
char *ret2 = malp("%s%s%s\n", ret, new, users);
|
||||||
|
free(ret);
|
||||||
|
free(new);
|
||||||
|
free(users);
|
||||||
|
ret = ret2;
|
||||||
|
|
||||||
|
stmt = stmt->statement.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ast_dump(AST *tlc) {
|
||||||
|
return ast_dumpc(tlc, tlc);
|
||||||
|
}
|
||||||
5
src/ast/dump.h
Normal file
5
src/ast/dump.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
char *ast_dump(union AST *chu);
|
||||||
|
char *ast_dumpc(union AST *tlc, union AST *chu);
|
||||||
161
src/ast/linearize.c
Normal file
161
src/ast/linearize.c
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
#include"linearize.h"
|
||||||
|
|
||||||
|
#include"ast.h"
|
||||||
|
#include"ntc.h"
|
||||||
|
#include"utils.h"
|
||||||
|
#include<stdlib.h>
|
||||||
|
|
||||||
|
static void ast_patch_in_chunk(AST *chunkOuter, AST *stmtBefore, AST *chunkInner, AST *stmtAfter) {
|
||||||
|
if(chunkInner->chunk.statementFirst) {
|
||||||
|
stmtBefore->statement.next = chunkInner->chunk.statementFirst;
|
||||||
|
|
||||||
|
for(AST *z = chunkInner->chunk.statementFirst; z; z = z->statement.next) {
|
||||||
|
if(!z->statement.next) {
|
||||||
|
z->statement.next = stmtAfter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stmtBefore->statement.next = stmtAfter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LOOPSTACKSIZE 64
|
||||||
|
struct LinearizeState {
|
||||||
|
size_t currentDepth;
|
||||||
|
size_t loopStackStart[LOOPSTACKSIZE];
|
||||||
|
size_t loopStackEnd[LOOPSTACKSIZE];
|
||||||
|
AST *loopAfters[LOOPSTACKSIZE];
|
||||||
|
};
|
||||||
|
static void ast_linearize_visitor_pre(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
static size_t nextLabelIdx = 0;
|
||||||
|
|
||||||
|
struct LinearizeState *state = ud;
|
||||||
|
|
||||||
|
AST *a = *aptr;
|
||||||
|
|
||||||
|
if(a->nodeKind == AST_STMT_IF) {
|
||||||
|
if(a->stmtIf.elss == NULL) {
|
||||||
|
ASTExprUnaryOp *notcond = calloc(1, sizeof(*notcond));
|
||||||
|
notcond->nodeKind = AST_EXPR_UNARY_OP;
|
||||||
|
notcond->operator = UNOP_NOT;
|
||||||
|
notcond->operand = a->stmtIf.expression;
|
||||||
|
notcond->type = a->stmtIf.expression->expression.type;
|
||||||
|
|
||||||
|
ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump));
|
||||||
|
jump->nodeKind = AST_STMT_JUMP;
|
||||||
|
jump->condition = (AST*) notcond;
|
||||||
|
jump->label = malp("$Lin%lu", nextLabelIdx++);
|
||||||
|
|
||||||
|
ASTStmtLabel *label = calloc(1, sizeof(ASTStmtLabel));
|
||||||
|
label->nodeKind = AST_STMT_LABEL;
|
||||||
|
label->name = strdup(jump->label);
|
||||||
|
|
||||||
|
if(stmtPrev) {
|
||||||
|
stmtPrev->statement.next = (AST*) jump;
|
||||||
|
} else {
|
||||||
|
chunk->chunk.statementFirst = (AST*) jump;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_patch_in_chunk(chunk, (AST*) jump, a->stmtIf.then, (AST*) label);
|
||||||
|
|
||||||
|
label->next = a->statement.next;
|
||||||
|
} else {
|
||||||
|
ASTExprUnaryOp *notcond = calloc(1, sizeof(*notcond));
|
||||||
|
notcond->nodeKind = AST_EXPR_UNARY_OP;
|
||||||
|
notcond->operator = UNOP_NOT;
|
||||||
|
notcond->operand = a->stmtIf.expression;
|
||||||
|
notcond->type = a->stmtIf.expression->expression.type;
|
||||||
|
|
||||||
|
ASTStmtJump *jump2Else = calloc(1, sizeof(ASTStmtJump));
|
||||||
|
jump2Else->nodeKind = AST_STMT_JUMP;
|
||||||
|
jump2Else->condition = (AST*) notcond;
|
||||||
|
jump2Else->label = malp("$Lin%lu", nextLabelIdx++);
|
||||||
|
|
||||||
|
ASTStmtJump *jump2End = calloc(1, sizeof(ASTStmtJump));
|
||||||
|
jump2End->nodeKind = AST_STMT_JUMP;
|
||||||
|
jump2End->label = malp("$Lin%lu", nextLabelIdx++);
|
||||||
|
|
||||||
|
ASTStmtLabel *labelElse = calloc(1, sizeof(ASTStmtLabel));
|
||||||
|
labelElse->nodeKind = AST_STMT_LABEL;
|
||||||
|
labelElse->name = strdup(jump2Else->label);
|
||||||
|
|
||||||
|
ASTStmtLabel *labelEnd = calloc(1, sizeof(ASTStmtLabel));
|
||||||
|
labelEnd->nodeKind = AST_STMT_LABEL;
|
||||||
|
labelEnd->name = strdup(jump2End->label);
|
||||||
|
|
||||||
|
if(stmtPrev) {
|
||||||
|
stmtPrev->statement.next = (AST*) jump2Else;
|
||||||
|
} else {
|
||||||
|
chunk->chunk.statementFirst = (AST*) jump2Else;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_patch_in_chunk(chunk, (AST*) jump2Else, a->stmtIf.then, (AST*) jump2End);
|
||||||
|
|
||||||
|
jump2End->next = (AST*) labelElse;
|
||||||
|
|
||||||
|
ast_patch_in_chunk(chunk, (AST*) labelElse, a->stmtIf.elss, (AST*) labelEnd);
|
||||||
|
|
||||||
|
labelEnd->next = a->statement.next;
|
||||||
|
}
|
||||||
|
} else if(a->nodeKind == AST_STMT_LOOP) {
|
||||||
|
size_t startIdx = nextLabelIdx++;
|
||||||
|
size_t endIdx = nextLabelIdx++;
|
||||||
|
|
||||||
|
ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump));
|
||||||
|
jump->nodeKind = AST_STMT_JUMP;
|
||||||
|
jump->condition = NULL;
|
||||||
|
jump->label = malp("$Lin%lu", startIdx);
|
||||||
|
|
||||||
|
ASTStmtLabel *startLabel = calloc(1, sizeof(ASTStmtLabel));
|
||||||
|
startLabel->nodeKind = AST_STMT_LABEL;
|
||||||
|
startLabel->name = strdup(jump->label);
|
||||||
|
|
||||||
|
ASTStmtLabel *endLabel = calloc(1, sizeof(ASTStmtLabel));
|
||||||
|
endLabel->nodeKind = AST_STMT_LABEL;
|
||||||
|
endLabel->name = malp("$Lin%lu", endIdx);
|
||||||
|
|
||||||
|
if(stmtPrev) {
|
||||||
|
stmtPrev->statement.next = (AST*) startLabel;
|
||||||
|
} else {
|
||||||
|
chunk->chunk.statementFirst = (AST*) startLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_patch_in_chunk(chunk, (AST*) startLabel, a->stmtLoop.body, (AST*) jump);
|
||||||
|
|
||||||
|
jump->next = (AST*) endLabel;
|
||||||
|
endLabel->next = a->statement.next;
|
||||||
|
|
||||||
|
state->currentDepth++;
|
||||||
|
state->loopStackStart[state->currentDepth - 1] = startIdx;
|
||||||
|
state->loopStackEnd[state->currentDepth - 1] = endIdx;
|
||||||
|
state->loopAfters[state->currentDepth - 1] = (AST*) endLabel;
|
||||||
|
} else if(a->nodeKind == AST_STMT_BREAK) {
|
||||||
|
ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump));
|
||||||
|
jump->nodeKind = AST_STMT_JUMP;
|
||||||
|
jump->condition = NULL;
|
||||||
|
jump->label = malp("$Lin%lu", state->loopStackEnd[state->currentDepth - 1]);
|
||||||
|
jump->next = a->statement.next;
|
||||||
|
*aptr = (AST*) jump;
|
||||||
|
} else if(a->nodeKind == AST_STMT_CONTINUE) {
|
||||||
|
ASTStmtJump *jump = calloc(1, sizeof(ASTStmtJump));
|
||||||
|
jump->nodeKind = AST_STMT_JUMP;
|
||||||
|
jump->condition = NULL;
|
||||||
|
jump->label = malp("$Lin%lu", state->loopStackStart[state->currentDepth - 1]);
|
||||||
|
jump->next = a->statement.next;
|
||||||
|
*aptr = (AST*) jump;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void ast_linearize_visitor_post(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
struct LinearizeState *state = ud;
|
||||||
|
|
||||||
|
AST *a = *aptr;
|
||||||
|
|
||||||
|
if(state->currentDepth && state->loopAfters[state->currentDepth - 1] == a) {
|
||||||
|
state->currentDepth--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ast_linearize(AST *tlc) {
|
||||||
|
struct LinearizeState state = {};
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, ast_linearize_visitor_pre, ast_linearize_visitor_post);
|
||||||
|
}
|
||||||
4
src/ast/linearize.h
Normal file
4
src/ast/linearize.h
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
void ast_linearize(union AST *tlc);
|
||||||
61
src/ast/scr.c
Normal file
61
src/ast/scr.c
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#include"sroa.h"
|
||||||
|
|
||||||
|
#include"ast.h"
|
||||||
|
#include"ntc.h"
|
||||||
|
#include"utils.h"
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<assert.h>
|
||||||
|
|
||||||
|
static bool is_sroa_candidate(ScopeItem *si) {
|
||||||
|
return si->type->type == TYPE_TYPE_RECORD && type_size(si->type) <= ntc_get_int_default("sroa-threshold", 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SCRState {
|
||||||
|
AST *tlc;
|
||||||
|
};
|
||||||
|
static void ast_scr_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
AST *n = *aptr;
|
||||||
|
|
||||||
|
struct SCRState *state = ud;
|
||||||
|
|
||||||
|
if(state->tlc != tlc) return;
|
||||||
|
|
||||||
|
if(n->nodeKind == AST_STMT_ASSIGN && n->stmtAssign.what->expression.type->type == TYPE_TYPE_RECORD) {
|
||||||
|
Type *rectype = n->stmtAssign.what->expression.type;
|
||||||
|
|
||||||
|
for(size_t f = 0; f < rectype->record.fieldCount; f++) {
|
||||||
|
ASTExprDot *dot1 = calloc(1, sizeof(*dot1));
|
||||||
|
dot1->nodeKind = AST_EXPR_DOT;
|
||||||
|
dot1->type = rectype->record.fieldTypes[f];
|
||||||
|
dot1->a = ast_deep_copy(n->stmtAssign.what);
|
||||||
|
dot1->b = strdup(rectype->record.fieldNames[f]);
|
||||||
|
|
||||||
|
ASTExprDot *dot2 = calloc(1, sizeof(*dot2));
|
||||||
|
dot2->nodeKind = AST_EXPR_DOT;
|
||||||
|
dot2->type = rectype->record.fieldTypes[f];
|
||||||
|
dot2->a = ast_deep_copy(n->stmtAssign.to);
|
||||||
|
dot2->b = strdup(rectype->record.fieldNames[f]);
|
||||||
|
|
||||||
|
ASTStmtAssign *assign = calloc(1, sizeof(*assign));
|
||||||
|
assign->nodeKind = AST_STMT_ASSIGN;
|
||||||
|
assign->what = (AST*) dot1;
|
||||||
|
assign->to = (AST*) dot2;
|
||||||
|
|
||||||
|
stmtPrev->statement.next = (AST*) assign;
|
||||||
|
stmtPrev = assign;
|
||||||
|
}
|
||||||
|
|
||||||
|
stmtPrev->statement.next = stmt->statement.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ast_secondclass_record(AST *tlc) {
|
||||||
|
struct SCRState state = {.tlc = tlc};
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, ast_scr_visitor, NULL);
|
||||||
|
|
||||||
|
if(ntc_get_int("pdbg")) {
|
||||||
|
char *astdump = ast_dump(tlc);
|
||||||
|
fprintf(stderr, "### SCR ###\n%s\n", astdump);
|
||||||
|
free(astdump);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/ast/scr.h
Normal file
12
src/ast/scr.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// In machine code structs/records are second-class concepts.
|
||||||
|
// Either the record gets split into its separate fields (SRoA),
|
||||||
|
// or it must be spilled into memory.
|
||||||
|
//
|
||||||
|
// In any case, many QoL features programmers love (assigning structs to
|
||||||
|
// structs or passing structs as arguments to functions) don't exist in
|
||||||
|
// machine code and must be converted to a valid but equivalent form.
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
void ast_secondclass_record(union AST *tlc);
|
||||||
91
src/ast/sroa.c
Normal file
91
src/ast/sroa.c
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#include"sroa.h"
|
||||||
|
|
||||||
|
#include"ast.h"
|
||||||
|
#include"ntc.h"
|
||||||
|
#include"utils.h"
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<assert.h>
|
||||||
|
|
||||||
|
static bool is_sroa_candidate(ScopeItem *si) {
|
||||||
|
return si->type->type == TYPE_TYPE_RECORD && type_size(si->type) <= ntc_get_int_default("sroa-threshold", 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DecomposeAutomaticRecordState {
|
||||||
|
AST *tlc;
|
||||||
|
ScopeItem *target;
|
||||||
|
ScopeItem **replacements;
|
||||||
|
};
|
||||||
|
static void ast_decompose_automatic_record_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
AST *n = *aptr;
|
||||||
|
|
||||||
|
struct DecomposeAutomaticRecordState *state = ud;
|
||||||
|
|
||||||
|
if(state->tlc != tlc) return;
|
||||||
|
|
||||||
|
if(n->nodeKind == AST_EXPR_DOT && n->exprDot.a->nodeKind == AST_EXPR_VAR && n->exprDot.a->exprVar.thing == state->target) {
|
||||||
|
size_t idx;
|
||||||
|
for(idx = 0; strcmp(state->target->type->record.fieldNames[idx], n->exprDot.b); idx++);
|
||||||
|
|
||||||
|
AST *ev = calloc(1, sizeof(ASTExprVar));
|
||||||
|
ev->nodeKind = AST_EXPR_VAR;
|
||||||
|
ev->expression.type = state->target->type->record.fieldTypes[idx];
|
||||||
|
ev->exprVar.thing = state->replacements[idx];
|
||||||
|
|
||||||
|
*aptr = (AST*) ev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void ast_decompose_automatic_record(AST *tlc, ScopeItem *target) {
|
||||||
|
assert(target->kind == SCOPEITEM_VAR);
|
||||||
|
assert(target->type->type == TYPE_TYPE_RECORD);
|
||||||
|
|
||||||
|
struct DecomposeAutomaticRecordState state = {
|
||||||
|
.tlc = tlc,
|
||||||
|
.target = target,
|
||||||
|
.replacements = calloc(target->type->record.fieldCount, sizeof(ScopeItem)),
|
||||||
|
};
|
||||||
|
|
||||||
|
tlc->chunk.vars = realloc(tlc->chunk.vars, sizeof(*tlc->chunk.vars) * (tlc->chunk.varCount + target->type->record.fieldCount));
|
||||||
|
|
||||||
|
for(size_t f = 0; f < target->type->record.fieldCount; f++) {
|
||||||
|
ScopeItem *si = calloc(1, sizeof(*si));
|
||||||
|
si->kind = SCOPEITEM_VAR;
|
||||||
|
si->type = target->type->record.fieldTypes[f];
|
||||||
|
si->data.var.name = malp("%s_sroa_%s", target->data.var.name, target->type->record.fieldNames[f]);
|
||||||
|
si->data.var.declaration = target->data.var.declaration;
|
||||||
|
state.replacements[f] = si;
|
||||||
|
|
||||||
|
tlc->chunk.vars[tlc->chunk.varCount++] = si;
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, ast_decompose_automatic_record_visitor, NULL);
|
||||||
|
|
||||||
|
free(state.replacements);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ast_sroa(AST *tlc) {
|
||||||
|
for(int i = 0; i < tlc->chunk.varCount; i++) {
|
||||||
|
ScopeItem *si = tlc->chunk.vars[i];
|
||||||
|
|
||||||
|
if(!is_sroa_candidate(si)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ast_is_scopeitem_referenced(tlc, si)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_decompose_automatic_record(tlc, si);
|
||||||
|
|
||||||
|
memmove(tlc->chunk.vars + i, tlc->chunk.vars + i + 1, sizeof(*tlc->chunk.vars) * (tlc->chunk.varCount - i - 1));
|
||||||
|
tlc->chunk.varCount--;
|
||||||
|
|
||||||
|
/* Restart */
|
||||||
|
i = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ntc_get_int("pdbg")) {
|
||||||
|
char *astdump = ast_dump(tlc);
|
||||||
|
fprintf(stderr, "### SROA ###\n%s\n", astdump);
|
||||||
|
free(astdump);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/ast/sroa.h
Normal file
4
src/ast/sroa.h
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
void ast_sroa(union AST *tlc);
|
||||||
102
src/ast/stack.c
Normal file
102
src/ast/stack.c
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#include"stack.h"
|
||||||
|
|
||||||
|
#include"ast.h"
|
||||||
|
#include"ntc.h"
|
||||||
|
#include"scope.h"
|
||||||
|
#include<assert.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include"x86/arch.h"
|
||||||
|
|
||||||
|
struct Spill2StackState {
|
||||||
|
AST *targetTLC;
|
||||||
|
ScopeItem *target; // can be NULL
|
||||||
|
size_t stackGrowth;
|
||||||
|
};
|
||||||
|
static void spill2stack_visitor(AST **aptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
struct Spill2StackState *this = ud;
|
||||||
|
|
||||||
|
if(tlc != this->targetTLC) {
|
||||||
|
// Don't do anything.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AST *a = *aptr;
|
||||||
|
|
||||||
|
if(a == tlc) {
|
||||||
|
a->chunk.stackReservation += this->stackGrowth;
|
||||||
|
} else if(a->nodeKind == AST_EXPR_VAR) {
|
||||||
|
|
||||||
|
if(a->exprVar.thing == this->target) {
|
||||||
|
// DO THE SPILL
|
||||||
|
|
||||||
|
ASTExprStackPointer *rsp = calloc(1, sizeof(*rsp));
|
||||||
|
rsp->nodeKind = AST_EXPR_STACK_POINTER;
|
||||||
|
rsp->type = type_u(8 * arch_gpr_size());
|
||||||
|
|
||||||
|
ASTExprPrimitive *offset = calloc(1, sizeof(*offset));
|
||||||
|
offset->nodeKind = AST_EXPR_PRIMITIVE;
|
||||||
|
offset->type = rsp->type;
|
||||||
|
offset->val = -this->stackGrowth; // This will be affected by the other part of this pass, so we must reverse
|
||||||
|
offset->stackGrowth = true;
|
||||||
|
|
||||||
|
ASTExprBinaryOp *bop = calloc(1, sizeof(*bop));
|
||||||
|
bop->nodeKind = AST_EXPR_BINARY_OP;
|
||||||
|
bop->type = rsp->type;
|
||||||
|
bop->operator = BINOP_ADD;
|
||||||
|
bop->operands[0] = (AST*) rsp;
|
||||||
|
bop->operands[1] = (AST*) offset;
|
||||||
|
|
||||||
|
ASTExprBinaryOp *deref = calloc(1, sizeof(*deref));
|
||||||
|
deref->nodeKind = AST_EXPR_BINARY_OP;
|
||||||
|
deref->type = a->expression.type;
|
||||||
|
deref->operator = BINOP_DEREF;
|
||||||
|
deref->operands[0] = (AST*) bop;
|
||||||
|
|
||||||
|
*aptr = (AST*) deref;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(a->nodeKind == AST_EXPR_PRIMITIVE && a->exprPrim.stackGrowth) {
|
||||||
|
|
||||||
|
// Guaranteed to not require more normalization
|
||||||
|
a->exprPrim.val += this->stackGrowth;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ast_spill_to_stack(AST *tlc, ScopeItem *vte) {
|
||||||
|
assert(tlc->nodeKind == AST_CHUNK);
|
||||||
|
assert(vte != NULL);
|
||||||
|
assert(vte->kind == SCOPEITEM_VAR);
|
||||||
|
|
||||||
|
if(ntc_get_int("pdbg")) {
|
||||||
|
fprintf(stderr, "### SPILLING %s ###\n", vte->data.var.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Spill2StackState state;
|
||||||
|
memset(&state, 0, sizeof(state));
|
||||||
|
state.target = vte;
|
||||||
|
state.targetTLC = tlc;
|
||||||
|
state.stackGrowth = (type_size(vte->type) + 7) & ~7;
|
||||||
|
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, spill2stack_visitor, NULL);
|
||||||
|
|
||||||
|
size_t vteIndex = 0;
|
||||||
|
while(tlc->chunk.vars[vteIndex] != vte) vteIndex++;
|
||||||
|
|
||||||
|
memmove(&tlc->chunk.vars[vteIndex], &tlc->chunk.vars[vteIndex + 1], sizeof(*tlc->chunk.vars) * (tlc->chunk.varCount - vteIndex - 1));
|
||||||
|
tlc->chunk.varCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ast_grow_stack_frame(AST *tlc, size_t bytes) {
|
||||||
|
if(bytes == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Spill2StackState state;
|
||||||
|
memset(&state, 0, sizeof(state));
|
||||||
|
state.target = NULL; // spill nothing
|
||||||
|
state.targetTLC = tlc;
|
||||||
|
state.stackGrowth = bytes;
|
||||||
|
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, spill2stack_visitor, NULL);
|
||||||
|
}
|
||||||
9
src/ast/stack.h
Normal file
9
src/ast/stack.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<stddef.h>
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
struct ScopeItem;
|
||||||
|
|
||||||
|
void ast_spill_to_stack(union AST *tlc, struct ScopeItem *vte);
|
||||||
|
void ast_grow_stack_frame(union AST *tlc, size_t bytes);
|
||||||
223
src/ast/usedef.c
Normal file
223
src/ast/usedef.c
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
#include"usedef.h"
|
||||||
|
|
||||||
|
#include"ast.h"
|
||||||
|
#include"ntc.h"
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<assert.h>
|
||||||
|
#include"reporting.h"
|
||||||
|
#include"scope.h"
|
||||||
|
|
||||||
|
static void rd_kill(ReachingDefs *a, ScopeItem *var) {
|
||||||
|
for(size_t i = a->defCount; i --> 0;) {
|
||||||
|
AST *def = a->defs[i];
|
||||||
|
|
||||||
|
assert(def->nodeKind == AST_STMT_ASSIGN);
|
||||||
|
assert(def->stmtAssign.what->nodeKind == AST_EXPR_VAR);
|
||||||
|
assert(def->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR);
|
||||||
|
|
||||||
|
if(def->stmtAssign.what->exprVar.thing == var) {
|
||||||
|
memmove(&a->defs[i], &a->defs[i + 1], sizeof(*a->defs) * (a->defCount - i - 1));
|
||||||
|
a->defCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rd_equal(ReachingDefs *a, ReachingDefs *b) {
|
||||||
|
if(a->defCount != b->defCount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < a->defCount; i++) {
|
||||||
|
if(a->defs[i] != b->defs[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compar_ptr(const void *a, const void *b) {
|
||||||
|
return *(uintptr_t*) a - *(uintptr_t*) b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rd_find(ReachingDefs *dest, union AST *ast) {
|
||||||
|
return !!bsearch(&ast, dest->defs, dest->defCount, sizeof(*dest->defs), compar_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rd_add(ReachingDefs *dest, union AST *ast) {
|
||||||
|
if(rd_find(dest, ast)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest->defs = realloc(dest->defs, sizeof(*dest->defs) * (++dest->defCount));
|
||||||
|
dest->defs[dest->defCount - 1] = ast;
|
||||||
|
qsort(dest->defs, dest->defCount, sizeof(*dest->defs), compar_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rd_union(ReachingDefs *dest, ReachingDefs *src) {
|
||||||
|
for(size_t i = 0; i < src->defCount; i++) {
|
||||||
|
rd_add(dest, src->defs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ReachingDefs rd_compute_in(AST *tlc, AST *stmt, AST *stmtPrev) {
|
||||||
|
ReachingDefs rd = {};
|
||||||
|
|
||||||
|
// The previous statement is a predecessor unless it's an unconditional jump statement
|
||||||
|
if(stmtPrev && (stmtPrev->nodeKind != AST_STMT_JUMP || stmtPrev->stmtJump.condition)) {
|
||||||
|
rd_union(&rd, &stmtPrev->statement.rd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a label statement, then all jumps to this statement are predecessors
|
||||||
|
if(stmt->nodeKind == AST_STMT_LABEL) {
|
||||||
|
for(AST *s = tlc->chunk.statementFirst; s; s = s->statement.next) {
|
||||||
|
if(s->nodeKind == AST_STMT_JUMP && !strcmp(s->stmtJump.label, stmt->stmtLabel.name)) {
|
||||||
|
rd_union(&rd, &s->statement.rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rd_step(AST *tlc, AST *stmt, AST *stmtPrev) {
|
||||||
|
stmt->statement.dirty = false;
|
||||||
|
|
||||||
|
ReachingDefs rd = rd_compute_in(tlc, stmt, stmtPrev);
|
||||||
|
|
||||||
|
if(stmt->nodeKind == AST_STMT_ASSIGN && stmt->stmtAssign.what->nodeKind == AST_EXPR_VAR && stmt->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||||
|
rd_kill(&rd, stmt->stmtAssign.what->exprVar.thing);
|
||||||
|
rd_add(&rd, stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!rd_equal(&rd, &stmt->statement.rd)) {
|
||||||
|
// Set dirty flag on all successors
|
||||||
|
|
||||||
|
// The next statement is a successor unless it's an unconditional jump statement
|
||||||
|
if(stmt->statement.next && (stmt->nodeKind != AST_STMT_JUMP || stmt->stmtJump.condition)) {
|
||||||
|
stmt->statement.next->statement.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a jump statement, the target label is a successor
|
||||||
|
if(stmt->nodeKind == AST_STMT_JUMP) {
|
||||||
|
AST *label = ast_get_label_by_name(tlc, stmt->stmtJump.label);
|
||||||
|
label->statement.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt->statement.rd = rd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usedef_generation_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
AST *n = *nptr;
|
||||||
|
|
||||||
|
if(n->nodeKind == AST_EXPR_VAR) {
|
||||||
|
ReachingDefs *rd = &stmt->statement.rd;
|
||||||
|
|
||||||
|
ScopeItem *si = n->exprVar.thing;
|
||||||
|
|
||||||
|
for(size_t rdi = 0; rdi < rd->defCount; rdi++) {
|
||||||
|
AST *def = rd->defs[rdi];
|
||||||
|
|
||||||
|
if(def->stmtAssign.what->exprVar.thing == si) {
|
||||||
|
UseDef *ud = calloc(1, sizeof(*ud));
|
||||||
|
ud->def = def;
|
||||||
|
ud->use = n;
|
||||||
|
ud->stmt = stmt;
|
||||||
|
|
||||||
|
if(!si->data.var.usedefFirst) {
|
||||||
|
si->data.var.usedefFirst = si->data.var.usedefLast = ud;
|
||||||
|
} else {
|
||||||
|
si->data.var.usedefLast->next = ud;
|
||||||
|
si->data.var.usedefLast = ud;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void ast_usedef_reset(AST *chu) {
|
||||||
|
for(size_t i = 0; i < chu->chunk.varCount; i++) {
|
||||||
|
ScopeItem *vte = chu->chunk.vars[i];
|
||||||
|
|
||||||
|
assert(vte->kind == SCOPEITEM_VAR);
|
||||||
|
|
||||||
|
vte->data.var.usedefFirst = NULL;
|
||||||
|
vte->data.var.usedefLast = NULL;
|
||||||
|
vte->data.var.liveRangeStart = NULL;
|
||||||
|
vte->data.var.liveRangeEnd = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(AST *s = chu->chunk.statementFirst; s; s = s->statement.next) {
|
||||||
|
if(s->nodeKind == AST_STMT_IF || s->nodeKind == AST_STMT_LOOP) {
|
||||||
|
stahp(0, 0, "UD-chain generation requires a completely linear IR");
|
||||||
|
}
|
||||||
|
|
||||||
|
s->statement.dirty = true;
|
||||||
|
|
||||||
|
s->statement.rd.defCount = 0;
|
||||||
|
free(s->statement.rd.defs);
|
||||||
|
s->statement.rd.defs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t rdsteps = 0;; rdsteps++) {
|
||||||
|
//fprintf(stderr, "RD step %lu\n", rdsteps);
|
||||||
|
|
||||||
|
AST *prev = NULL;
|
||||||
|
AST *dirty = NULL;
|
||||||
|
|
||||||
|
// Find at least one dirty statement
|
||||||
|
for(AST *s = chu->chunk.statementFirst; s; prev = s, s = s->statement.next) {
|
||||||
|
if(s->statement.dirty) {
|
||||||
|
dirty = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!dirty) {
|
||||||
|
// Completed reaching definition computation
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rd_step(chu, dirty, prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_visitor(&chu, NULL, NULL, chu, chu, NULL, usedef_generation_visitor, NULL);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < chu->chunk.varCount; i++) {
|
||||||
|
ScopeItem *vte = chu->chunk.vars[i];
|
||||||
|
|
||||||
|
assert(vte->kind == SCOPEITEM_VAR);
|
||||||
|
|
||||||
|
assert(!!vte->data.var.usedefFirst == !!vte->data.var.usedefLast);
|
||||||
|
|
||||||
|
if(vte->data.var.usedefFirst) {
|
||||||
|
vte->data.var.liveRangeStart = vte->data.var.usedefFirst->stmt;
|
||||||
|
vte->data.var.liveRangeEnd = vte->data.var.usedefLast->stmt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix liveRangeStart and/or liveRangeEnd depending on goto targets
|
||||||
|
for(AST *s = chu->chunk.statementFirst; s; s = s->statement.next) {
|
||||||
|
if(s->nodeKind == AST_STMT_JUMP) {
|
||||||
|
AST *target = ast_get_label_by_name(chu, s->stmtJump.label);
|
||||||
|
|
||||||
|
for(size_t sii = 0; sii < chu->chunk.varCount; sii++) {
|
||||||
|
ScopeItem *si = chu->chunk.vars[sii];
|
||||||
|
|
||||||
|
assert(si->data.var.declaration != NULL && "All local vars must have defined declaration location");
|
||||||
|
|
||||||
|
if(ast_stmt_is_after(chu, si->data.var.liveRangeEnd, s) == 0 && ast_stmt_is_after(chu, target, si->data.var.liveRangeEnd) == 0 && ast_stmt_is_after(chu, si->data.var.declaration, target) == 0) {
|
||||||
|
|
||||||
|
si->data.var.liveRangeEnd = s;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ntc_get_int("pdbg")) {
|
||||||
|
char *astdump = ast_dump(chu);
|
||||||
|
fprintf(stderr, "### USEDEF GENERATED ###\n%s\n", astdump);
|
||||||
|
free(astdump);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/ast/usedef.h
Normal file
4
src/ast/usedef.h
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
void ast_usedef_reset(union AST *chu);
|
||||||
327
src/cg.c
327
src/cg.c
@@ -1,327 +0,0 @@
|
|||||||
#include"cg.h"
|
|
||||||
|
|
||||||
#include<stdlib.h>
|
|
||||||
#include<signal.h>
|
|
||||||
#include<string.h>
|
|
||||||
#include<assert.h>
|
|
||||||
|
|
||||||
#define REGS 4
|
|
||||||
static const char *regs[REGS][3] = {{"al", "ax", "eax"}, {"bl", "bx", "ebx"}, {"cl", "cx", "ecx"}, {"dl", "dx", "edx"}, {"sil", "si", "esi"}, {"dil", "di", "edi"}};
|
|
||||||
|
|
||||||
static const char *BINOP_SIMPLE_INSTRS[] = {[BINOP_ADD] = "add", [BINOP_SUB] = "sub", [BINOP_BITWISE_AND] = "and", [BINOP_BITWISE_OR] = "or", [BINOP_BITWISE_XOR] = "xor"};
|
|
||||||
|
|
||||||
static size_t nextLocalLabel = 0;
|
|
||||||
|
|
||||||
#define LOOPSTACKSIZE 64
|
|
||||||
static size_t loopStackStart[LOOPSTACKSIZE];
|
|
||||||
static size_t loopStackEnd[LOOPSTACKSIZE];
|
|
||||||
static size_t loopStackIdx;
|
|
||||||
|
|
||||||
static const char *direct(int size) {
|
|
||||||
switch(size) {
|
|
||||||
case 1: return "db";
|
|
||||||
case 2: return "dw";
|
|
||||||
case 4: return "dd";
|
|
||||||
case 8: return "dq";
|
|
||||||
}
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *spec(int size) {
|
|
||||||
switch(size) {
|
|
||||||
case 1: return "byte";
|
|
||||||
case 2: return "word";
|
|
||||||
case 4: return "dword";
|
|
||||||
case 8: return "qword";
|
|
||||||
}
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *specexpr(AST *e) {
|
|
||||||
return spec(type_size(e->expression.type));
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *xv(VarTableEntry *v) {
|
|
||||||
assert(v->kind == VARTABLEENTRY_VAR);
|
|
||||||
|
|
||||||
#define XVBUFS 8
|
|
||||||
#define XVBUFSZ 8
|
|
||||||
static char bufs[XVBUFS][XVBUFSZ];
|
|
||||||
static int bufidx = 0;
|
|
||||||
|
|
||||||
char *ret = bufs[bufidx];
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
snprintf(ret, XVBUFSZ, "@%i", v->data.var.color);
|
|
||||||
#else
|
|
||||||
snprintf(ret, XVBUFSZ, "%s", regs[v->data.var.color][2]);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bufidx = (bufidx + 1) % XVBUFS;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *xj(BinaryOp op) {
|
|
||||||
switch(op) {
|
|
||||||
case BINOP_EQUAL: return "e";
|
|
||||||
case BINOP_NEQUAL: return "ne";
|
|
||||||
default: return "wtf";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *xop(AST *e) {
|
|
||||||
#define XOPBUFS 16
|
|
||||||
#define XOPBUFSZ 24
|
|
||||||
static char bufs[XOPBUFS][XOPBUFSZ];
|
|
||||||
static int bufidx = 0;
|
|
||||||
|
|
||||||
char *ret = bufs[bufidx];
|
|
||||||
|
|
||||||
if(e->nodeKind == AST_EXPR_VAR) {
|
|
||||||
VarTableEntry *v = e->exprVar.thing;
|
|
||||||
|
|
||||||
if(v->kind == VARTABLEENTRY_VAR) {
|
|
||||||
return xv(v);
|
|
||||||
} else if(v->kind == VARTABLEENTRY_SYMBOL) {
|
|
||||||
snprintf(ret, XOPBUFSZ, "[%s]", v->data.symbol.name);
|
|
||||||
} else abort();
|
|
||||||
} else if(e->nodeKind == AST_EXPR_PRIMITIVE) {
|
|
||||||
snprintf(ret, XOPBUFSZ, "%i", e->exprPrim.val);
|
|
||||||
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
|
|
||||||
snprintf(ret, XOPBUFSZ, "[%s + %s]",
|
|
||||||
e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
|
|
||||||
xv(e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing));
|
|
||||||
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
|
|
||||||
snprintf(ret, XOPBUFSZ, "[%s + %i * %s]",
|
|
||||||
e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
|
|
||||||
e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val,
|
|
||||||
xv(e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing));
|
|
||||||
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL) {
|
|
||||||
snprintf(ret, XOPBUFSZ, "%s", e->exprUnOp.operand->exprVar.thing->data.symbol.name);
|
|
||||||
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_VAR) {
|
|
||||||
snprintf(ret, XOPBUFSZ, "[%s]", xv(e->exprUnOp.operand->exprVar.thing));
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bufidx = (bufidx + 1) % XOPBUFS;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cg_chunk(AST *a) {
|
|
||||||
AST *s = a->chunk.statementFirst;
|
|
||||||
|
|
||||||
// Potentially complex pattern matching
|
|
||||||
while(s) {
|
|
||||||
if(s->nodeKind == AST_STMT_EXT_SECTION) {
|
|
||||||
|
|
||||||
Token t = s->stmtExtSection.name;
|
|
||||||
printf("section %.*s\n", (int) t.length, t.content);
|
|
||||||
|
|
||||||
} else if(s->nodeKind == AST_STMT_EXT_ORG) {
|
|
||||||
|
|
||||||
printf("org %lu\n", s->stmtExtOrg.val);
|
|
||||||
|
|
||||||
} else if(s->nodeKind == AST_STMT_DECL && s->stmtDecl.thing->kind == VARTABLEENTRY_SYMBOL) {
|
|
||||||
VarTableEntry *v = s->stmtDecl.thing;
|
|
||||||
|
|
||||||
if(v->data.symbol.isExternal) {
|
|
||||||
printf("extern %s\n", v->data.symbol.name);
|
|
||||||
} else {
|
|
||||||
if(!v->data.symbol.isLocal) {
|
|
||||||
printf("global %s\n", v->data.symbol.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(s->stmtDecl.expression) {
|
|
||||||
puts("A");
|
|
||||||
} else {
|
|
||||||
printf("%s resb %lu\n", v->data.symbol.name, type_size(s->stmtDecl.thing->type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if(s->nodeKind == AST_STMT_ASSIGN) {
|
|
||||||
|
|
||||||
if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0]) && (s->stmtAssign.to->exprBinOp.operator == BINOP_ADD || s->stmtAssign.to->exprBinOp.operator == BINOP_SUB) && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && s->stmtAssign.to->exprBinOp.operands[1]->exprPrim.val == 1) {
|
|
||||||
|
|
||||||
// inc or dec
|
|
||||||
|
|
||||||
static const char *instrs[] = {"inc", "dec"};
|
|
||||||
printf("%s %s %s\n", instrs[s->stmtAssign.to->exprBinOp.operator == BINOP_SUB], specexpr(s->stmtAssign.what), xop(s->stmtAssign.what));
|
|
||||||
|
|
||||||
} else if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && s->stmtAssign.to->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR && s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
|
|
||||||
|
|
||||||
printf("lea %s, [%s + %s]\n",
|
|
||||||
xv(s->stmtAssign.what->exprVar.thing),
|
|
||||||
xv(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing),
|
|
||||||
xv(s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing));
|
|
||||||
|
|
||||||
} else if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && s->stmtAssign.to->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
|
|
||||||
|
|
||||||
printf("lea %s, [%s + %s]\n",
|
|
||||||
xv(s->stmtAssign.what->exprVar.thing),
|
|
||||||
s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
|
|
||||||
xv(s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
printf("mov %s, %s\n", xop(s->stmtAssign.what), xop(s->stmtAssign.to));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if(s->nodeKind == AST_STMT_LOOP) {
|
|
||||||
|
|
||||||
size_t lbl0 = nextLocalLabel++;
|
|
||||||
size_t lbl1 = nextLocalLabel++;
|
|
||||||
|
|
||||||
loopStackStart[loopStackIdx] = lbl0;
|
|
||||||
loopStackEnd[loopStackIdx] = lbl1;
|
|
||||||
loopStackIdx++;
|
|
||||||
|
|
||||||
printf(".L%lu:\n", lbl0);
|
|
||||||
|
|
||||||
cg_chunk(s->stmtLoop.body);
|
|
||||||
|
|
||||||
printf("jmp .L%lu\n", lbl0);
|
|
||||||
|
|
||||||
printf(".L%lu:\n", lbl1);
|
|
||||||
|
|
||||||
loopStackIdx--;
|
|
||||||
|
|
||||||
} else if(s->nodeKind == AST_STMT_BREAK) {
|
|
||||||
|
|
||||||
printf("jmp .L%lu\n", loopStackEnd[loopStackIdx - 1]);
|
|
||||||
|
|
||||||
} else if(s->nodeKind == AST_STMT_CONTINUE) {
|
|
||||||
|
|
||||||
printf("jmp .L%lu\n", loopStackStart[loopStackIdx - 1]);
|
|
||||||
|
|
||||||
} else if(s->nodeKind == AST_STMT_IF) {
|
|
||||||
|
|
||||||
assert(s->stmtIf.expression->nodeKind == AST_EXPR_BINARY_OP && binop_is_comparison(s->stmtIf.expression->exprBinOp.operator));
|
|
||||||
|
|
||||||
size_t lbl = nextLocalLabel++;
|
|
||||||
|
|
||||||
printf("cmp %s %s, %s\n", specexpr(s->stmtIf.expression->exprBinOp.operands[0]), xop(s->stmtIf.expression->exprBinOp.operands[0]), xop(s->stmtIf.expression->exprBinOp.operands[1]));
|
|
||||||
printf("j%s .L%lu\n", xj(binop_comp_opposite(s->stmtIf.expression->exprBinOp.operator)), lbl);
|
|
||||||
|
|
||||||
cg_chunk(s->stmtIf.then);
|
|
||||||
|
|
||||||
printf(".L%lu:\n", lbl);
|
|
||||||
|
|
||||||
} else if(s->nodeKind == AST_STMT_EXPR) {
|
|
||||||
|
|
||||||
AST *e = s->stmtExpr.expr;
|
|
||||||
|
|
||||||
if(e->nodeKind == AST_EXPR_CALL) {
|
|
||||||
puts("push eax");
|
|
||||||
puts("push ecx");
|
|
||||||
puts("push edx");
|
|
||||||
|
|
||||||
int argCount = e->exprCall.what->expression.type->function.argCount;
|
|
||||||
|
|
||||||
size_t argSize = 0;
|
|
||||||
|
|
||||||
for(int i = argCount - 1; i >= 0; i--) {
|
|
||||||
printf("push %s\n", xop(e->exprCall.args[i]));
|
|
||||||
|
|
||||||
argSize += (type_size(e->exprCall.args[i]->expression.type) + 3) & ~3;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(e->exprCall.what->nodeKind == AST_EXPR_VAR && e->exprCall.what->exprVar.thing->kind == VARTABLEENTRY_SYMBOL);
|
|
||||||
|
|
||||||
printf("call %s\n", e->exprCall.what->exprVar.thing->data.symbol.name);
|
|
||||||
|
|
||||||
printf("add esp, %lu\n", argSize);
|
|
||||||
|
|
||||||
puts("pop edx");
|
|
||||||
puts("pop ecx");
|
|
||||||
puts("pop eax");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
s = s->statement.next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Welsh-Powell graph coloring */
|
|
||||||
static int comparator(const void *A, const void *B) {
|
|
||||||
VarTableEntry *const *a = A;
|
|
||||||
VarTableEntry *const *b = B;
|
|
||||||
return ((*a)->data.var.degree * (*a)->data.var.priority) - ((*b)->data.var.degree * (*b)->data.var.priority);
|
|
||||||
}
|
|
||||||
void cg_go(AST *a) {
|
|
||||||
typedef VarTableEntry *Adjacency[2];
|
|
||||||
|
|
||||||
size_t adjCount = 0;
|
|
||||||
Adjacency *adjs = malloc(sizeof(*adjs) * adjCount);
|
|
||||||
|
|
||||||
VarTableEntry **vars = a->chunk.vars;
|
|
||||||
|
|
||||||
for(size_t v1i = 0; v1i < a->chunk.varCount; v1i++) {
|
|
||||||
for(size_t v2i = 0; v2i < a->chunk.varCount; v2i++) {
|
|
||||||
if(v1i == v2i) continue;
|
|
||||||
|
|
||||||
VarTableEntry *v1 = vars[v1i];
|
|
||||||
VarTableEntry *v2 = vars[v2i];
|
|
||||||
|
|
||||||
/* 1D intersection test */
|
|
||||||
// if((v1->data.var.start >= v2->data.var.start && v1->data.var.start <= v2->data.var.end)
|
|
||||||
// || (v1->data.var.end >= v2->data.var.start && v1->data.var.end <= v2->data.var.end)) {
|
|
||||||
if(
|
|
||||||
(ast_stmt_is_after(a, v1->data.var.usedefFirst->stmt, v2->data.var.usedefFirst->stmt) == 1
|
|
||||||
&& ast_stmt_is_after(a, v2->data.var.usedefLast->stmt, v1->data.var.usedefFirst->stmt) == 1)
|
|
||||||
||
|
|
||||||
(ast_stmt_is_after(a, v1->data.var.usedefLast->stmt, v2->data.var.usedefFirst->stmt) == 1
|
|
||||||
&& ast_stmt_is_after(a, v2->data.var.usedefLast->stmt, v1->data.var.usedefLast->stmt) == 1)
|
|
||||||
) {
|
|
||||||
VarTableEntry *min = v1 < v2 ? v1 : v2;
|
|
||||||
VarTableEntry *max = v1 < v2 ? v2 : v1;
|
|
||||||
|
|
||||||
for(size_t a = 0; a < adjCount; a++) {
|
|
||||||
if(adjs[a][0] == min && adjs[a][1] == max) {
|
|
||||||
goto cont;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
adjs = realloc(adjs, sizeof(*adjs) * ++adjCount);
|
|
||||||
adjs[adjCount - 1][0] = min;
|
|
||||||
adjs[adjCount - 1][1] = max;
|
|
||||||
|
|
||||||
cont:;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(size_t a = 0; a < adjCount; a++) {
|
|
||||||
adjs[a][0]->data.var.degree++;
|
|
||||||
adjs[a][1]->data.var.degree++;
|
|
||||||
}
|
|
||||||
|
|
||||||
qsort(vars, a->chunk.varCount, sizeof(*vars), comparator);
|
|
||||||
|
|
||||||
/* Welsh plow my ass */
|
|
||||||
for(int v = 0; v < a->chunk.varCount; v++) {
|
|
||||||
for(int c = 0;; c++) {
|
|
||||||
for(int a = 0; a < adjCount; a++) {
|
|
||||||
if(adjs[a][0] == vars[v] && adjs[a][1]->data.var.color == c) {
|
|
||||||
goto nextColor;
|
|
||||||
} else if(adjs[a][1] == vars[v] && adjs[a][0]->data.var.color == c) {
|
|
||||||
goto nextColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vars[v]->data.var.color = c;
|
|
||||||
break;
|
|
||||||
|
|
||||||
nextColor:;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(adjs);
|
|
||||||
|
|
||||||
cg_chunk(a);
|
|
||||||
|
|
||||||
free(vars);
|
|
||||||
}
|
|
||||||
8
src/cg.h
8
src/cg.h
@@ -1,8 +0,0 @@
|
|||||||
#ifndef H_CG
|
|
||||||
#define H_CG
|
|
||||||
|
|
||||||
#include"ast.h"
|
|
||||||
|
|
||||||
void cg_go(union AST*);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
141
src/dstr.c
141
src/dstr.c
@@ -1,141 +0,0 @@
|
|||||||
#include"dstr.h"
|
|
||||||
|
|
||||||
#include<stdarg.h>
|
|
||||||
#include<stdlib.h>
|
|
||||||
#include<string.h>
|
|
||||||
#include<stdio.h>
|
|
||||||
|
|
||||||
static int ilen(int i) {
|
|
||||||
if(i == 0) return 1;
|
|
||||||
|
|
||||||
int ret = 0;
|
|
||||||
if(i < 0) {
|
|
||||||
ret = 1;
|
|
||||||
i = -i;
|
|
||||||
}
|
|
||||||
while(i > 0) {
|
|
||||||
ret++;
|
|
||||||
i = i / 10;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *myitoa(int src) {
|
|
||||||
static char ret[12];
|
|
||||||
snprintf(ret, 12, "%i", src);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
size_t length;
|
|
||||||
char data[];
|
|
||||||
} dstrInternal;
|
|
||||||
|
|
||||||
dstr dstrempty() {
|
|
||||||
dstrInternal *i = malloc(sizeof(dstrInternal) + 1);
|
|
||||||
i->length = 0;
|
|
||||||
i->data[0] = '\0';
|
|
||||||
return (dstr) i + sizeof(dstrInternal);
|
|
||||||
}
|
|
||||||
|
|
||||||
dstr dstrz(const char *src) {
|
|
||||||
size_t len = strlen(src);
|
|
||||||
dstrInternal *i = malloc(sizeof(dstrInternal) + len + 1);
|
|
||||||
i->length = len;
|
|
||||||
memcpy(i->data, src, len + 1);
|
|
||||||
return (dstr) i + sizeof(dstrInternal);
|
|
||||||
}
|
|
||||||
|
|
||||||
dstr dstrfmt(dstr original, const char *fmt, ...) {
|
|
||||||
dstrInternal *originalInternal = (dstrInternal*) (original - sizeof(dstrInternal));
|
|
||||||
|
|
||||||
const char *start = fmt;
|
|
||||||
|
|
||||||
va_list list;
|
|
||||||
va_start(list, fmt);
|
|
||||||
|
|
||||||
size_t totalLength = 0;
|
|
||||||
|
|
||||||
while(*fmt) {
|
|
||||||
if(*fmt == '%') {
|
|
||||||
switch(*++fmt) {
|
|
||||||
case 's':
|
|
||||||
totalLength += strlen(va_arg(list, char*));
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
if(va_arg(list, int)) totalLength++;
|
|
||||||
break;
|
|
||||||
case 'S': {
|
|
||||||
dstrInternal *i = (dstrInternal*) (va_arg(list, dstr) - sizeof(dstrInternal));
|
|
||||||
totalLength += i->length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'i':
|
|
||||||
totalLength += ilen(va_arg(list, int));
|
|
||||||
break;
|
|
||||||
default: {
|
|
||||||
totalLength++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else totalLength++;
|
|
||||||
|
|
||||||
fmt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
va_end(list);
|
|
||||||
|
|
||||||
fmt = start;
|
|
||||||
|
|
||||||
originalInternal = realloc(originalInternal, sizeof(dstrInternal) + originalInternal->length + totalLength + 1);
|
|
||||||
|
|
||||||
va_start(list, fmt);
|
|
||||||
|
|
||||||
char *dst = originalInternal->data + originalInternal->length;
|
|
||||||
originalInternal->length += totalLength;
|
|
||||||
originalInternal->data[originalInternal->length] = 0;
|
|
||||||
|
|
||||||
while(*fmt) {
|
|
||||||
if(*fmt == '%') {
|
|
||||||
switch(*++fmt) {
|
|
||||||
case 's': {
|
|
||||||
char *asdfasdf = va_arg(list, char*);
|
|
||||||
strcpy(dst, asdfasdf);
|
|
||||||
dst += strlen(asdfasdf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'c': {
|
|
||||||
int c = va_arg(list, int);
|
|
||||||
if(c) {
|
|
||||||
*(dst++) = c;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'S': {
|
|
||||||
dstrInternal *i = (dstrInternal*) (va_arg(list, dstr) - sizeof(dstrInternal));
|
|
||||||
memcpy(dst, i->data, i->length);
|
|
||||||
dst += i->length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'i': {
|
|
||||||
const char *asdf = myitoa(va_arg(list, int));
|
|
||||||
strcpy(dst, asdf);
|
|
||||||
dst += strlen(asdf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
*(dst++) = *fmt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*(dst++) = *fmt;
|
|
||||||
}
|
|
||||||
fmt++;
|
|
||||||
}
|
|
||||||
va_end(list);
|
|
||||||
|
|
||||||
return (dstr) originalInternal + sizeof(dstrInternal);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dstrfree(dstr s) {
|
|
||||||
free(s - sizeof(dstrInternal));
|
|
||||||
}
|
|
||||||
15
src/dstr.h
15
src/dstr.h
@@ -1,15 +0,0 @@
|
|||||||
#ifndef _DSTR_H
|
|
||||||
#define _DSTR_H
|
|
||||||
|
|
||||||
#include<stddef.h>
|
|
||||||
|
|
||||||
/* Originally used sds, but it didn't support OpenWatcom. This isn't as optimized, but it's good enough. */
|
|
||||||
|
|
||||||
typedef char *dstr;
|
|
||||||
|
|
||||||
dstr dstrempty();
|
|
||||||
dstr dstrraw(const char*);
|
|
||||||
dstr dstrfmt(dstr, const char*, ...);
|
|
||||||
void dstrfree(dstr);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
96
src/lexer.c
96
src/lexer.c
@@ -27,16 +27,32 @@ char *TOKEN_NAMES[] = {
|
|||||||
"'break'",
|
"'break'",
|
||||||
"','",
|
"','",
|
||||||
"'&'",
|
"'&'",
|
||||||
|
"'&&'",
|
||||||
"'|'",
|
"'|'",
|
||||||
|
"'||'",
|
||||||
"'^'",
|
"'^'",
|
||||||
"'~'",
|
"'~'",
|
||||||
"'=='",
|
"'=='",
|
||||||
"'['",
|
"'['",
|
||||||
"']'",
|
"']'",
|
||||||
"'?'",
|
"'?'",
|
||||||
"string"
|
"string",
|
||||||
"'!='",
|
"'!='",
|
||||||
"'!'"
|
"'!'",
|
||||||
|
"'continue'",
|
||||||
|
"'return'",
|
||||||
|
"'->'",
|
||||||
|
"'<='",
|
||||||
|
"'>='",
|
||||||
|
"'<'",
|
||||||
|
"'>'",
|
||||||
|
"'*^'",
|
||||||
|
"'record'",
|
||||||
|
"'.'",
|
||||||
|
"'as'",
|
||||||
|
"'use'",
|
||||||
|
"'else'",
|
||||||
|
"'null'",
|
||||||
};
|
};
|
||||||
|
|
||||||
static int isAlpha(int c) {
|
static int isAlpha(int c) {
|
||||||
@@ -55,7 +71,7 @@ static int isWS(int c) {
|
|||||||
return c == ' ' || c == '\n' || c == '\r' || c == '\b' || c == '\t';
|
return c == ' ' || c == '\n' || c == '\r' || c == '\b' || c == '\t';
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t currentRow = 0;
|
static size_t currentRow = 1;
|
||||||
static size_t currentColumn = 0;
|
static size_t currentColumn = 0;
|
||||||
static int ungetted = EOF;
|
static int ungetted = EOF;
|
||||||
|
|
||||||
@@ -116,15 +132,31 @@ Token nct_tokenize(FILE *f) {
|
|||||||
return tok;
|
return tok;
|
||||||
} else if(c == '-') {
|
} else if(c == '-') {
|
||||||
tok.type = TOKEN_MINUS;
|
tok.type = TOKEN_MINUS;
|
||||||
|
int c = nextc(f);
|
||||||
|
if(c == '>') {
|
||||||
|
tok.type = TOKEN_ARROW;
|
||||||
|
} else ungetc(c, f);
|
||||||
return tok;
|
return tok;
|
||||||
} else if(c == '*') {
|
} else if(c == '*') {
|
||||||
tok.type = TOKEN_STAR;
|
tok.type = TOKEN_STAR;
|
||||||
|
int c = nextc(f);
|
||||||
|
if(c == '^') {
|
||||||
|
tok.type = TOKEN_STAR_CARET;
|
||||||
|
} else ungetc(c, f);
|
||||||
return tok;
|
return tok;
|
||||||
} else if(c == '&') {
|
} else if(c == '&') {
|
||||||
tok.type = TOKEN_AMPERSAND;
|
tok.type = TOKEN_AMPERSAND;
|
||||||
|
int c = nextc(f);
|
||||||
|
if(c == '&') {
|
||||||
|
tok.type = TOKEN_DOUBLE_AMPERSAND;
|
||||||
|
} else ungetc(c, f);
|
||||||
return tok;
|
return tok;
|
||||||
} else if(c == '|') {
|
} else if(c == '|') {
|
||||||
tok.type = TOKEN_VERTICAL_BAR;
|
tok.type = TOKEN_VERTICAL_BAR;
|
||||||
|
int c = nextc(f);
|
||||||
|
if(c == '|') {
|
||||||
|
tok.type = TOKEN_DOUBLE_VERTICAL_BAR;
|
||||||
|
} else ungetc(c, f);
|
||||||
return tok;
|
return tok;
|
||||||
} else if(c == '^') {
|
} else if(c == '^') {
|
||||||
tok.type = TOKEN_CARET;
|
tok.type = TOKEN_CARET;
|
||||||
@@ -148,6 +180,20 @@ Token nct_tokenize(FILE *f) {
|
|||||||
tok.type = TOKEN_EXCLAMATION_EQUALS;
|
tok.type = TOKEN_EXCLAMATION_EQUALS;
|
||||||
} else ungetc(c, f);
|
} else ungetc(c, f);
|
||||||
return tok;
|
return tok;
|
||||||
|
} else if(c == '<') {
|
||||||
|
tok.type = TOKEN_LESS;
|
||||||
|
int c = nextc(f);
|
||||||
|
if(c == '=') {
|
||||||
|
tok.type = TOKEN_LEQUAL;
|
||||||
|
} else ungetc(c, f);
|
||||||
|
return tok;
|
||||||
|
} else if(c == '>') {
|
||||||
|
tok.type = TOKEN_GREATER;
|
||||||
|
int c = nextc(f);
|
||||||
|
if(c == '=') {
|
||||||
|
tok.type = TOKEN_GEQUAL;
|
||||||
|
} else ungetc(c, f);
|
||||||
|
return tok;
|
||||||
} else if(c == '/') {
|
} else if(c == '/') {
|
||||||
int c = nextc(f);
|
int c = nextc(f);
|
||||||
if(c == '*') { /* This is a comment; skip. */
|
if(c == '*') { /* This is a comment; skip. */
|
||||||
@@ -172,6 +218,9 @@ Token nct_tokenize(FILE *f) {
|
|||||||
} else if(c == ',') {
|
} else if(c == ',') {
|
||||||
tok.type = TOKEN_COMMA;
|
tok.type = TOKEN_COMMA;
|
||||||
return tok;
|
return tok;
|
||||||
|
} else if(c == '.') {
|
||||||
|
tok.type = TOKEN_DOT;
|
||||||
|
return tok;
|
||||||
} else if(c == '"') {
|
} else if(c == '"') {
|
||||||
int capacity = 5;
|
int capacity = 5;
|
||||||
char *content = malloc(capacity);
|
char *content = malloc(capacity);
|
||||||
@@ -188,6 +237,7 @@ Token nct_tokenize(FILE *f) {
|
|||||||
|
|
||||||
if(c == '0') c = 0;
|
if(c == '0') c = 0;
|
||||||
else if(c == 'n') c = '\n';
|
else if(c == 'n') c = '\n';
|
||||||
|
else if(c == 'r') c = '\r';
|
||||||
else if(c == 't') c = '\t';
|
else if(c == 't') c = '\t';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,14 +250,14 @@ Token nct_tokenize(FILE *f) {
|
|||||||
tok.content = content;
|
tok.content = content;
|
||||||
tok.length = i;
|
tok.length = i;
|
||||||
return tok;
|
return tok;
|
||||||
} else if(isAlpha(c) || c == '@') {
|
} else if(isAlpha(c) || c == '@' || c == '_') {
|
||||||
int capacity = 5;
|
int capacity = 5;
|
||||||
char *content = malloc(capacity);
|
char *content = malloc(capacity);
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
content[i++] = c;
|
content[i++] = c;
|
||||||
|
|
||||||
while(c = nextc(f), (isAlphanum(c) || c == '@')) {
|
while(c = nextc(f), (isAlphanum(c) || c == '@' || c == '_')) {
|
||||||
if(i == capacity - 1) {
|
if(i == capacity - 1) {
|
||||||
content = realloc(content, capacity += 4);
|
content = realloc(content, capacity += 4);
|
||||||
}
|
}
|
||||||
@@ -242,6 +292,30 @@ Token nct_tokenize(FILE *f) {
|
|||||||
free(content);
|
free(content);
|
||||||
tok.type = TOKEN_CONTINUE;
|
tok.type = TOKEN_CONTINUE;
|
||||||
return tok;
|
return tok;
|
||||||
|
} else if(!strcmp(content, "return")) {
|
||||||
|
free(content);
|
||||||
|
tok.type = TOKEN_RETURN;
|
||||||
|
return tok;
|
||||||
|
} else if(!strcmp(content, "record")) {
|
||||||
|
free(content);
|
||||||
|
tok.type = TOKEN_RECORD;
|
||||||
|
return tok;
|
||||||
|
} else if(!strcmp(content, "as")) {
|
||||||
|
free(content);
|
||||||
|
tok.type = TOKEN_AS;
|
||||||
|
return tok;
|
||||||
|
} else if(!strcmp(content, "use")) {
|
||||||
|
free(content);
|
||||||
|
tok.type = TOKEN_USE;
|
||||||
|
return tok;
|
||||||
|
} else if(!strcmp(content, "else")) {
|
||||||
|
free(content);
|
||||||
|
tok.type = TOKEN_ELSE;
|
||||||
|
return tok;
|
||||||
|
} else if(!strcmp(content, "null")) {
|
||||||
|
free(content);
|
||||||
|
tok.type = TOKEN_NULL;
|
||||||
|
return tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
tok.type = TOKEN_IDENTIFIER;
|
tok.type = TOKEN_IDENTIFIER;
|
||||||
@@ -271,7 +345,7 @@ Token nct_tokenize(FILE *f) {
|
|||||||
|
|
||||||
while(c = nextc(f), (isNum(c) || (base > 10 && c >= 'A' && c < ('A' + base - 10)))) {
|
while(c = nextc(f), (isNum(c) || (base > 10 && c >= 'A' && c < ('A' + base - 10)))) {
|
||||||
if(i == 31) {
|
if(i == 31) {
|
||||||
stahp(1, 6180, "Numbers have a maximum size of 31.");
|
stahp_token(&tok, "Numbers have a maximum size of 31.");
|
||||||
}
|
}
|
||||||
|
|
||||||
content[i++] = c;
|
content[i++] = c;
|
||||||
@@ -301,6 +375,9 @@ Token *nct_lex(FILE *f) {
|
|||||||
size_t length = 8, index = 0;
|
size_t length = 8, index = 0;
|
||||||
Token *list = malloc(sizeof(*list) * length);
|
Token *list = malloc(sizeof(*list) * length);
|
||||||
|
|
||||||
|
currentRow = 1;
|
||||||
|
currentColumn = 0;
|
||||||
|
|
||||||
while(1) {
|
while(1) {
|
||||||
list[index] = nct_tokenize(f);
|
list[index] = nct_tokenize(f);
|
||||||
|
|
||||||
@@ -318,3 +395,10 @@ Token *nct_lex(FILE *f) {
|
|||||||
|
|
||||||
return NULL; /* Doesn't reach here. */
|
return NULL; /* Doesn't reach here. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nct_lex_free(Token *tokens) {
|
||||||
|
for(Token *t = tokens; t->type != TOKEN_EOF; t++) {
|
||||||
|
if(t->content) free(t->content);
|
||||||
|
}
|
||||||
|
free(tokens);
|
||||||
|
}
|
||||||
|
|||||||
18
src/lexer.h
18
src/lexer.h
@@ -27,7 +27,9 @@ typedef enum {
|
|||||||
TOKEN_BREAK,
|
TOKEN_BREAK,
|
||||||
TOKEN_COMMA,
|
TOKEN_COMMA,
|
||||||
TOKEN_AMPERSAND,
|
TOKEN_AMPERSAND,
|
||||||
|
TOKEN_DOUBLE_AMPERSAND,
|
||||||
TOKEN_VERTICAL_BAR,
|
TOKEN_VERTICAL_BAR,
|
||||||
|
TOKEN_DOUBLE_VERTICAL_BAR,
|
||||||
TOKEN_CARET,
|
TOKEN_CARET,
|
||||||
TOKEN_TILDE,
|
TOKEN_TILDE,
|
||||||
TOKEN_DOUBLE_EQUALS,
|
TOKEN_DOUBLE_EQUALS,
|
||||||
@@ -38,9 +40,22 @@ typedef enum {
|
|||||||
TOKEN_EXCLAMATION_EQUALS,
|
TOKEN_EXCLAMATION_EQUALS,
|
||||||
TOKEN_EXCLAMATION,
|
TOKEN_EXCLAMATION,
|
||||||
TOKEN_CONTINUE,
|
TOKEN_CONTINUE,
|
||||||
|
TOKEN_RETURN,
|
||||||
|
TOKEN_ARROW,
|
||||||
|
TOKEN_LEQUAL,
|
||||||
|
TOKEN_GEQUAL,
|
||||||
|
TOKEN_LESS,
|
||||||
|
TOKEN_GREATER,
|
||||||
|
TOKEN_STAR_CARET,
|
||||||
|
TOKEN_RECORD,
|
||||||
|
TOKEN_DOT,
|
||||||
|
TOKEN_AS,
|
||||||
|
TOKEN_USE,
|
||||||
|
TOKEN_ELSE,
|
||||||
|
TOKEN_NULL,
|
||||||
} TokenKind;
|
} TokenKind;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct Token {
|
||||||
TokenKind type;
|
TokenKind type;
|
||||||
int row, column;
|
int row, column;
|
||||||
|
|
||||||
@@ -50,5 +65,6 @@ typedef struct {
|
|||||||
|
|
||||||
Token nct_tokenize(FILE*);
|
Token nct_tokenize(FILE*);
|
||||||
Token *nct_lex(FILE*);
|
Token *nct_lex(FILE*);
|
||||||
|
void nct_lex_free(Token *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
87
src/ntc.c
87
src/ntc.c
@@ -1,42 +1,115 @@
|
|||||||
#include<errno.h>
|
#include<errno.h>
|
||||||
#include<string.h>
|
#include<string.h>
|
||||||
#include<stdlib.h>
|
#include<stdlib.h>
|
||||||
|
#include<libgen.h>
|
||||||
|
|
||||||
#include"lexer.h"
|
#include"lexer.h"
|
||||||
#include"parse.h"
|
#include"parse.h"
|
||||||
#include"ntc.h"
|
#include"ntc.h"
|
||||||
#include"reporting.h"
|
#include"reporting.h"
|
||||||
#include"cg.h"
|
#include"utils.h"
|
||||||
|
|
||||||
static int argc;
|
static int argc;
|
||||||
static char **argv;
|
static char **argv;
|
||||||
|
|
||||||
const char* ntc_get_arg(const char *name) {
|
static const char **includePaths;
|
||||||
for(int i = 1; i < argc; i++) {
|
const char **ntc_get_import_paths() {
|
||||||
if(strstr(argv[i], name) == argv[i]) {
|
return (const char**) includePaths;
|
||||||
return argv[i] + strlen(name) + 1;
|
}
|
||||||
|
|
||||||
|
static const char *ntc_get_arg_from(size_t *i, const char *name) {
|
||||||
|
for(; *i < argc; (*i)++) {
|
||||||
|
if(strstr(argv[*i], name) == argv[*i]) {
|
||||||
|
return argv[*i] + strlen(name) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* ntc_get_arg(const char *name) {
|
||||||
|
size_t i = 1;
|
||||||
|
return ntc_get_arg_from(&i, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
intmax_t ntc_get_int(const char *name) {
|
||||||
|
return ntc_get_int_default(name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
intmax_t ntc_get_int_default(const char *name, intmax_t def) {
|
||||||
|
const char *val = ntc_get_arg(name);
|
||||||
|
|
||||||
|
if(!val) {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
long result = 0;
|
||||||
|
if(!unstupid_strtol(val, (char**) &val, 0, &result)) {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc_, char **argv_) {
|
int main(int argc_, char **argv_) {
|
||||||
argc = argc_;
|
argc = argc_;
|
||||||
argv = argv_;
|
argv = argv_;
|
||||||
|
|
||||||
|
if(!arch_verify_target()) {
|
||||||
|
stahp(0, 0, "Unknown architecture %s", ntc_get_arg("target"));
|
||||||
|
}
|
||||||
|
|
||||||
const char *in = ntc_get_arg("in");
|
const char *in = ntc_get_arg("in");
|
||||||
|
|
||||||
|
size_t includePathsCount = 1;
|
||||||
|
includePaths = malloc(sizeof(*includePaths));
|
||||||
|
includePaths[0] = strdup(in ? dirname(strdup(in)) : ".");
|
||||||
|
for(size_t i = 1; i < argc; i++) {
|
||||||
|
const char *path = ntc_get_arg_from(&i, "inc");
|
||||||
|
|
||||||
|
if(!path) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
includePaths = realloc(includePaths, sizeof(*includePaths) * (++includePathsCount));
|
||||||
|
includePaths[includePathsCount - 1] = path;
|
||||||
|
}
|
||||||
|
|
||||||
FILE *f = in ? fopen(in, "rb") : stdin;
|
FILE *f = in ? fopen(in, "rb") : stdin;
|
||||||
|
|
||||||
|
if(!f) {
|
||||||
|
stahp(0, 0, "Failed to read input (%s)", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
arch_init();
|
||||||
|
|
||||||
Token *tokens = nct_lex(f);
|
Token *tokens = nct_lex(f);
|
||||||
|
|
||||||
if(in) fclose(f);
|
if(in) fclose(f);
|
||||||
|
|
||||||
AST *chunk = nct_parse(tokens);
|
AST *sects = nct_parse(tokens);
|
||||||
|
|
||||||
free(tokens);
|
free(tokens);
|
||||||
|
|
||||||
cg_go(chunk);
|
if(ntc_get_int("pdbg")) {
|
||||||
|
char *astdump = ast_dump(sects);
|
||||||
|
fprintf(stderr, "### ORIGINAL ###\n%s\n", astdump);
|
||||||
|
free(astdump);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_segmented_dereference(sects);
|
||||||
|
ast_secondclass_record(sects);
|
||||||
|
ast_sroa(sects);
|
||||||
|
|
||||||
|
ast_linearize(sects);
|
||||||
|
|
||||||
|
arch_normalize_pre(sects);
|
||||||
|
|
||||||
|
arch_normalize(sects);
|
||||||
|
while(!cg_attempt_sections(sects)) {
|
||||||
|
arch_normalize(sects);
|
||||||
|
}
|
||||||
|
|
||||||
|
cg_go_sections(sects);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/ntc.h
20
src/ntc.h
@@ -1,6 +1,24 @@
|
|||||||
#ifndef NTC_H
|
#ifndef NTC_H
|
||||||
#define NTC_H
|
#define NTC_H
|
||||||
|
|
||||||
|
#include<stddef.h>
|
||||||
|
#include<stdint.h>
|
||||||
|
#include<stdbool.h>
|
||||||
|
|
||||||
|
const char **ntc_get_import_paths();
|
||||||
|
|
||||||
const char* ntc_get_arg(const char *name);
|
const char* ntc_get_arg(const char *name);
|
||||||
|
|
||||||
#endif
|
intmax_t ntc_get_int(const char *name);
|
||||||
|
intmax_t ntc_get_int_default(const char *name, intmax_t def);
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
void arch_init();
|
||||||
|
bool arch_verify_target();
|
||||||
|
int arch_ptr_size();
|
||||||
|
void arch_normalize_pre(union AST *tlc);
|
||||||
|
void arch_normalize(union AST *tlc);
|
||||||
|
int cg_attempt_sections(union AST *tlc);
|
||||||
|
void cg_go_sections(union AST *tlc);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
75
src/optims.c
75
src/optims.c
@@ -1,75 +0,0 @@
|
|||||||
#include"optims.h"
|
|
||||||
|
|
||||||
#include<assert.h>
|
|
||||||
|
|
||||||
// Currently performs only copy propagation.
|
|
||||||
// But CP is NECESSARY, otherwise it creates too many variables
|
|
||||||
// that are unable to be coalesced by the regallocator
|
|
||||||
|
|
||||||
void optim_chunk(ASTChunk *chu) {
|
|
||||||
/*AST *s = chu->statementFirst, *sPrev = NULL;
|
|
||||||
while(s) {
|
|
||||||
if(s->nodeKind == AST_STMT_ASSIGN && s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_VAR) {
|
|
||||||
VarTableEntry *dst = ((AST*) s->stmtAssign.what)->exprVar.thing;
|
|
||||||
VarTableEntry *src = ((AST*) s->stmtAssign.to)->exprVar.thing;
|
|
||||||
|
|
||||||
if(dst->kind == VARTABLEENTRY_VAR && src->kind == VARTABLEENTRY_VAR) {
|
|
||||||
// Find reaching source definition
|
|
||||||
|
|
||||||
UseDef *srcUD = src->data.var.usedefFirst;
|
|
||||||
while(srcUD && srcUD->use != s->stmtAssign.to) {
|
|
||||||
srcUD = srcUD->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!srcUD) {
|
|
||||||
goto copypropfail;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find first use of this def
|
|
||||||
|
|
||||||
UseDef *dstUDPrev = NULL;
|
|
||||||
UseDef *dstUD = dst->data.var.usedefFirst;
|
|
||||||
while(dstUD->def != s) {
|
|
||||||
dstUDPrev = dstUD;
|
|
||||||
dstUD = dstUD->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update all definitions
|
|
||||||
|
|
||||||
while(dstUD && dstUD->def == s) {
|
|
||||||
((AST*) dstUD->use)->exprVar.thing = src;
|
|
||||||
|
|
||||||
UseDef *next = dstUD->next;
|
|
||||||
|
|
||||||
dstUD->def = srcUD->def;
|
|
||||||
dstUD->next = srcUD->next;
|
|
||||||
srcUD->next = dstUD;
|
|
||||||
|
|
||||||
dstUD = next;
|
|
||||||
|
|
||||||
if(dstUDPrev) {
|
|
||||||
dstUDPrev->next = dstUD;
|
|
||||||
} else {
|
|
||||||
dst->data.var.usedefFirst = dstUD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!dstUD) {
|
|
||||||
// dst was never used again -> DELETE ASSIGNMENT COMPLETELY
|
|
||||||
|
|
||||||
if(sPrev) {
|
|
||||||
sPrev->statement.next = s->statement.next;
|
|
||||||
// TODO: free
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
recalc_lifespan(dst);
|
|
||||||
recalc_lifespan(src);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
copypropfail:
|
|
||||||
sPrev = s;
|
|
||||||
s = s->statement.next;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include"ast.h"
|
|
||||||
|
|
||||||
void optim_chunk(ASTChunk*);
|
|
||||||
1552
src/parse.c
1552
src/parse.c
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
#ifndef NCTREF_PARSE_H
|
#ifndef NCTREF_PARSE_H
|
||||||
#define NCTREF_PARSE_H
|
#define NCTREF_PARSE_H
|
||||||
|
|
||||||
#include"ast.h"
|
#include"ast/ast.h"
|
||||||
|
|
||||||
AST *nct_parse(Token*);
|
AST *nct_parse(Token*);
|
||||||
|
|
||||||
|
|||||||
@@ -3,17 +3,44 @@
|
|||||||
#include<stdarg.h>
|
#include<stdarg.h>
|
||||||
#include<stdio.h>
|
#include<stdio.h>
|
||||||
#include<stdlib.h>
|
#include<stdlib.h>
|
||||||
|
#include"ast/ast.h"
|
||||||
|
#include"lexer.h"
|
||||||
|
|
||||||
|
static void stahp_va(int row, int column, const char *error, va_list l) {
|
||||||
|
fprintf(stderr, "error %i:%i: ", row, column);
|
||||||
|
vfprintf(stderr, error, l);
|
||||||
|
fputc('\n', stderr);
|
||||||
|
}
|
||||||
|
|
||||||
/* Abort immediately on first error (for now) */
|
|
||||||
void stahp(int row, int column, const char *error, ...) {
|
void stahp(int row, int column, const char *error, ...) {
|
||||||
va_list l;
|
va_list l;
|
||||||
va_start(l, error);
|
va_start(l, error);
|
||||||
|
|
||||||
fprintf(stderr, "error %i:%i: ", row, column);
|
stahp_va(row, column, error, l);
|
||||||
vfprintf(stderr, error, l);
|
|
||||||
fputc('\n', stderr);
|
|
||||||
|
|
||||||
va_end(l);
|
va_end(l);
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void stahp_node(union AST *node, const char *error, ...) {
|
||||||
|
va_list l;
|
||||||
|
va_start(l, error);
|
||||||
|
|
||||||
|
stahp_va(node->row, node->col, error, l);
|
||||||
|
|
||||||
|
va_end(l);
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stahp_token(struct Token *tok, const char *error, ...) {
|
||||||
|
va_list l;
|
||||||
|
va_start(l, error);
|
||||||
|
|
||||||
|
stahp_va(tok->row, tok->column, error, l);
|
||||||
|
|
||||||
|
va_end(l);
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
#ifndef NCTREF_REPORTING_H
|
#ifndef NCTREF_REPORTING_H
|
||||||
#define NCTREF_REPORTING_H
|
#define NCTREF_REPORTING_H
|
||||||
|
|
||||||
#ifndef _GNUC
|
#ifndef __GNUC__
|
||||||
#define __attribute__(x)
|
#define __attribute__(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
struct Token;
|
||||||
|
|
||||||
void __attribute__((noreturn)) stahp(int, int, const char*, ...);
|
void __attribute__((noreturn)) stahp(int, int, const char*, ...);
|
||||||
|
void __attribute__((noreturn)) stahp_node(union AST*, const char*, ...);
|
||||||
|
void __attribute__((noreturn)) stahp_token(struct Token*, const char*, ...);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
93
src/scope.c
Normal file
93
src/scope.c
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#include"scope.h"
|
||||||
|
|
||||||
|
#include"utils.h"
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<string.h>
|
||||||
|
#include<assert.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
#include"x86/arch.h"
|
||||||
|
|
||||||
|
Scope *scope_new(Scope *parent) {
|
||||||
|
Scope *ret = calloc(1, sizeof(*ret));
|
||||||
|
ret->parent = parent;
|
||||||
|
ret->count = 0;
|
||||||
|
ret->names = NULL;
|
||||||
|
ret->data = NULL;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeItem *scope_get(Scope *this, const char *name) {
|
||||||
|
for(size_t v = 0; v < this->count; v++) {
|
||||||
|
if(!strcmp(name, this->names[v])) return this->data[v];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeItem *scope_find(Scope *this, const char *name) {
|
||||||
|
Scope *tbl = this;
|
||||||
|
while(tbl) {
|
||||||
|
ScopeItem *entry = scope_get(tbl, name);
|
||||||
|
if(entry) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
tbl = tbl->parent;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeItem *scope_set(Scope *this, const char *name, ScopeItem *e) {
|
||||||
|
name = strdup(name);
|
||||||
|
|
||||||
|
this->names = realloc(this->names, sizeof(*this->names) * (this->count + 1));
|
||||||
|
this->data = realloc(this->data, sizeof(*this->data) * (this->count + 1));
|
||||||
|
this->names[this->count] = name;
|
||||||
|
this->data[this->count] = e;
|
||||||
|
this->count++;
|
||||||
|
if(e->kind == SCOPEITEM_VAR) e->data.var.name = name;
|
||||||
|
e->owner = this;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeItem *scope_find_int(Scope *scope, intmax_t val, const char *suffix) {
|
||||||
|
char buf[64];
|
||||||
|
snprintf(buf, sizeof(buf), "%li%s", val, suffix ? suffix : "");
|
||||||
|
|
||||||
|
return scope_find(scope, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scope *scope_merge(Scope *child) {
|
||||||
|
Scope *parent = child->parent;
|
||||||
|
|
||||||
|
parent->names = realloc(parent->names, sizeof(*parent->names) * (parent->count + child->count));
|
||||||
|
parent->data = realloc(parent->data, sizeof(*parent->data) * (parent->count + child->count));
|
||||||
|
|
||||||
|
for(size_t i = 0; i < child->count; i++) {
|
||||||
|
child->data[i]->owner = parent;
|
||||||
|
|
||||||
|
parent->names[parent->count] = child->names[i];
|
||||||
|
parent->data[parent->count] = child->data[i];
|
||||||
|
|
||||||
|
parent->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//free(child->names);
|
||||||
|
//free(child->data);
|
||||||
|
//free(child);
|
||||||
|
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vte_precolor(ScopeItem *vte, int class, int color) {
|
||||||
|
assert(vte->kind == SCOPEITEM_VAR && "vte must be var");
|
||||||
|
assert(!vte->data.var.precolored && "already precolored");
|
||||||
|
|
||||||
|
if(type_size(vte->type) > 0) {
|
||||||
|
assert(type_size(vte->type) == REG_CLASSES[class].rsS[color] && "Sizes must match in precoloring");
|
||||||
|
}
|
||||||
|
|
||||||
|
vte->data.var.precolored = true;
|
||||||
|
vte->data.var.preclassed = true;
|
||||||
|
vte->data.var.registerClass = class;
|
||||||
|
vte->data.var.color = color;
|
||||||
|
}
|
||||||
105
src/scope.h
Normal file
105
src/scope.h
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#ifndef NCTREF_VARTABLE_H
|
||||||
|
#define NCTREF_VARTABLE_H
|
||||||
|
|
||||||
|
#include"types.h"
|
||||||
|
#include<stdbool.h>
|
||||||
|
|
||||||
|
struct Token;
|
||||||
|
union AST;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SCOPEITEM_SYMBOL, SCOPEITEM_VAR, SCOPEITEM_TYPE, SCOPEITEM_CEXPR
|
||||||
|
} ScopeItemKind;
|
||||||
|
|
||||||
|
union AST;
|
||||||
|
typedef struct UseDef {
|
||||||
|
union AST *def; // assign stmt
|
||||||
|
union AST *use; // corresponding AST_EXPR_VAR
|
||||||
|
union AST *stmt; // whole using stmt
|
||||||
|
struct UseDef *next;
|
||||||
|
} UseDef;
|
||||||
|
|
||||||
|
typedef struct ReachingDefs {
|
||||||
|
size_t defCount;
|
||||||
|
union AST **defs;
|
||||||
|
} ReachingDefs;
|
||||||
|
|
||||||
|
struct Scope;
|
||||||
|
typedef struct ScopeItem {
|
||||||
|
Type *type;
|
||||||
|
|
||||||
|
struct Scope *owner;
|
||||||
|
|
||||||
|
ScopeItemKind kind;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
char isLocal;
|
||||||
|
char isExternal;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct Scope *scope;
|
||||||
|
struct Token *rangeTokens;
|
||||||
|
size_t startTokI;
|
||||||
|
size_t endTokI;
|
||||||
|
} genfunc;
|
||||||
|
} symbol;
|
||||||
|
struct {
|
||||||
|
// For debugging
|
||||||
|
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
// Register allocation
|
||||||
|
|
||||||
|
// vars in loops have higher priority
|
||||||
|
// a more advanced approach would be to use weights for different colors (e.g. multipliers "should" be in eax)
|
||||||
|
uint8_t priority;
|
||||||
|
int16_t color, degree;
|
||||||
|
bool precolored, preclassed;
|
||||||
|
int registerClass;
|
||||||
|
|
||||||
|
// Used by ast_usedef_pass
|
||||||
|
//ReachingDefs *reachingDefs;
|
||||||
|
union AST *declaration;
|
||||||
|
|
||||||
|
UseDef *usedefFirst;
|
||||||
|
UseDef *usedefLast;
|
||||||
|
|
||||||
|
union AST *liveRangeStart;
|
||||||
|
union AST *liveRangeEnd;
|
||||||
|
} var;
|
||||||
|
struct {
|
||||||
|
Type *ptr;
|
||||||
|
} type;
|
||||||
|
struct {
|
||||||
|
// cexpr is used for expression parametization as opposed to type parametrization
|
||||||
|
// I don't like the idea of having a special ScopeItem kind for these, but all other places were worse
|
||||||
|
const char *paramName;
|
||||||
|
size_t paramIdx;
|
||||||
|
|
||||||
|
// If the cexpr has been parametrized (as opposed to just being a symbol), this field will be non-NULL
|
||||||
|
union AST *concrete;
|
||||||
|
} cexpr;
|
||||||
|
} data;
|
||||||
|
} ScopeItem;
|
||||||
|
|
||||||
|
typedef struct Scope {
|
||||||
|
struct Scope *parent;
|
||||||
|
|
||||||
|
size_t count;
|
||||||
|
const char **names;
|
||||||
|
ScopeItem **data;
|
||||||
|
} Scope;
|
||||||
|
|
||||||
|
Scope *scope_new(Scope*);
|
||||||
|
ScopeItem *scope_get(Scope*, const char*);
|
||||||
|
ScopeItem *scope_find(Scope*, const char*);
|
||||||
|
ScopeItem *scope_set(Scope*, const char*, ScopeItem*);
|
||||||
|
|
||||||
|
ScopeItem *scope_find_int(Scope*, intmax_t, const char*);
|
||||||
|
|
||||||
|
Scope *scope_merge(Scope *child);
|
||||||
|
|
||||||
|
void vte_precolor(ScopeItem *vte, int resclass, int color);
|
||||||
|
|
||||||
|
#endif
|
||||||
239
src/types.c
239
src/types.c
@@ -4,6 +4,10 @@
|
|||||||
#include<stdlib.h>
|
#include<stdlib.h>
|
||||||
#include<string.h>
|
#include<string.h>
|
||||||
#include<stdint.h>
|
#include<stdint.h>
|
||||||
|
#include"ast/ast.h"
|
||||||
|
#include"reporting.h"
|
||||||
|
#include<assert.h>
|
||||||
|
#include"x86/arch.h"
|
||||||
|
|
||||||
#include"ntc.h"
|
#include"ntc.h"
|
||||||
|
|
||||||
@@ -20,9 +24,19 @@ Type *primitive_parse(const char *src) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePrimitive *ret = malloc(sizeof(*ret));
|
if(!strcmp(src, "ugpr")) {
|
||||||
|
return type_u(8 * arch_gpr_size());
|
||||||
|
} else if(!strcmp(src, "sgpr")) {
|
||||||
|
return type_s(8 * arch_gpr_size());
|
||||||
|
} else if(!strcmp(src, "umem")) {
|
||||||
|
return type_u(arch_memory_width());
|
||||||
|
} else if(!strcmp(src, "smem")) {
|
||||||
|
return type_s(arch_memory_width());
|
||||||
|
}
|
||||||
|
|
||||||
|
TypePrimitive *ret = calloc(1, sizeof(*ret));
|
||||||
ret->type = TYPE_TYPE_PRIMITIVE;
|
ret->type = TYPE_TYPE_PRIMITIVE;
|
||||||
ret->src = src;
|
ret->src = strdup(src);
|
||||||
|
|
||||||
if(*src == 'n') {
|
if(*src == 'n') {
|
||||||
src++;
|
src++;
|
||||||
@@ -72,6 +86,11 @@ Type *primitive_parse(const char *src) {
|
|||||||
ret->vector = 1;
|
ret->vector = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(*src) {
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
ret->next = primitiveDatabase[hash];
|
ret->next = primitiveDatabase[hash];
|
||||||
primitiveDatabase[hash] = ret;
|
primitiveDatabase[hash] = ret;
|
||||||
|
|
||||||
@@ -91,9 +110,20 @@ size_t type_size(Type *t) {
|
|||||||
|
|
||||||
return w;
|
return w;
|
||||||
} else if(t->type == TYPE_TYPE_POINTER || t->type == TYPE_TYPE_FUNCTION) {
|
} else if(t->type == TYPE_TYPE_POINTER || t->type == TYPE_TYPE_FUNCTION) {
|
||||||
return 4;
|
return arch_ptr_size();
|
||||||
} else if(t->type == TYPE_TYPE_ARRAY) {
|
} else if(t->type == TYPE_TYPE_ARRAY) {
|
||||||
return type_size(t->array.of) * t->array.length;
|
return type_size(t->array.of) * t->array.length;
|
||||||
|
} else if(t->type == TYPE_TYPE_RECORD) {
|
||||||
|
size_t max = 0;
|
||||||
|
|
||||||
|
for(size_t f = 0; f < t->record.fieldCount; f++) {
|
||||||
|
size_t end = t->record.fieldOffsets[f] + type_size(t->record.fieldTypes[f]);
|
||||||
|
if(max < end) {
|
||||||
|
max = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
abort();
|
abort();
|
||||||
@@ -116,7 +146,37 @@ int type_equal(Type *O, Type *T) {
|
|||||||
} else if(O->type == TYPE_TYPE_POINTER) {
|
} else if(O->type == TYPE_TYPE_POINTER) {
|
||||||
return type_equal(O->pointer.of, T->pointer.of);
|
return type_equal(O->pointer.of, T->pointer.of);
|
||||||
} else if(O->type == TYPE_TYPE_ARRAY) {
|
} else if(O->type == TYPE_TYPE_ARRAY) {
|
||||||
return type_equal(O->array.of, T->array.of) && O->array.length == T->array.length;
|
if(!type_equal(O->array.of, T->array.of)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(O->array.lengthIsGeneric != T->array.lengthIsGeneric) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(O->array.lengthIsGeneric) {
|
||||||
|
return O->array.lengthGenericParamIdx == T->array.lengthGenericParamIdx && !strcmp(O->array.lengthGenericParamName, T->array.lengthGenericParamName);
|
||||||
|
} else {
|
||||||
|
return O->array.length == T->array.length;
|
||||||
|
}
|
||||||
|
} else if(O->type == TYPE_TYPE_FUNCTION) {
|
||||||
|
if(!type_equal(O->function.ret, T->function.ret)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(O->function.argCount != T->function.argCount) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < O->function.argCount; i++) {
|
||||||
|
if(!type_equal(O->function.args[i], T->function.args[i])) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't compare argument names
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Consider nominal typing. */
|
/* Consider nominal typing. */
|
||||||
@@ -126,12 +186,16 @@ int type_equal(Type *O, Type *T) {
|
|||||||
|
|
||||||
/* TODO: cache */
|
/* TODO: cache */
|
||||||
Type *type_pointer_wrap(Type *t) {
|
Type *type_pointer_wrap(Type *t) {
|
||||||
TypePointer *ret = malloc(sizeof(*ret));
|
TypePointer *ret = calloc(1, sizeof(*ret));
|
||||||
ret->type = TYPE_TYPE_POINTER;
|
ret->type = TYPE_TYPE_POINTER;
|
||||||
ret->of = t;
|
ret->of = t;
|
||||||
return (Type*) ret;
|
return (Type*) ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int type_is_number(Type *t) {
|
||||||
|
return t->type == TYPE_TYPE_POINTER || t->type == TYPE_TYPE_PRIMITIVE;
|
||||||
|
}
|
||||||
|
|
||||||
int type_is_castable(Type *from, Type *to) {
|
int type_is_castable(Type *from, Type *to) {
|
||||||
if(type_equal(from, to)) return 2;
|
if(type_equal(from, to)) return 2;
|
||||||
|
|
||||||
@@ -147,9 +211,170 @@ int type_is_castable(Type *from, Type *to) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(from->type == TYPE_TYPE_PRIMITIVE && to->type == TYPE_TYPE_POINTER) {
|
if(from->type == TYPE_TYPE_PRIMITIVE && to->type == TYPE_TYPE_POINTER || from->type == TYPE_TYPE_POINTER && to->type == TYPE_TYPE_PRIMITIVE) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool type_is_generic(Type *t) {
|
||||||
|
if(t->type == TYPE_TYPE_GENERIC) {
|
||||||
|
return true;
|
||||||
|
} else if(t->type == TYPE_TYPE_FUNCTION) {
|
||||||
|
if(type_is_generic(t->function.ret)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < t->function.argCount; i++) {
|
||||||
|
if(type_is_generic(t->function.args[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(t->type == TYPE_TYPE_RECORD) {
|
||||||
|
for(int i = 0; i < t->record.fieldCount; i++) {
|
||||||
|
if(type_is_generic(t->record.fieldTypes[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(t->type == TYPE_TYPE_POINTER) {
|
||||||
|
return type_is_generic(t->pointer.of);
|
||||||
|
} else if(t->type == TYPE_TYPE_ARRAY) {
|
||||||
|
return type_is_generic(t->array.of) || t->array.lengthIsGeneric;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *type_parametrize(Type *t, Scope *scope) {
|
||||||
|
if(t->type == TYPE_TYPE_RECORD) {
|
||||||
|
t = type_shallow_copy(t);
|
||||||
|
|
||||||
|
for(size_t f = 0; f < t->record.fieldCount; f++) {
|
||||||
|
t->record.fieldTypes[f] = type_parametrize(t->record.fieldTypes[f], scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!type_is_generic(t)) {
|
||||||
|
// Now that everything is concrete we may set the field offsets
|
||||||
|
|
||||||
|
size_t nextOffset = 0;
|
||||||
|
|
||||||
|
for(size_t f = 0; f < t->record.fieldCount; f++) {
|
||||||
|
t->record.fieldOffsets[f] = nextOffset;
|
||||||
|
|
||||||
|
nextOffset += type_size(t->record.fieldTypes[f]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(t->type == TYPE_TYPE_FUNCTION) {
|
||||||
|
t = type_shallow_copy(t);
|
||||||
|
|
||||||
|
t->function.ret = type_parametrize(t->function.ret, scope);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < t->function.argCount; i++) {
|
||||||
|
t->function.args[i] = type_parametrize(t->function.args[i], scope);
|
||||||
|
}
|
||||||
|
} else if(t->type == TYPE_TYPE_GENERIC) {
|
||||||
|
ScopeItem *vte = scope_find_int(scope, t->generic.paramIdx, "t");
|
||||||
|
|
||||||
|
if(vte) {
|
||||||
|
assert(vte->kind == SCOPEITEM_TYPE);
|
||||||
|
return vte->data.type.ptr;
|
||||||
|
}
|
||||||
|
} else if(t->type == TYPE_TYPE_POINTER) {
|
||||||
|
t = type_shallow_copy(t);
|
||||||
|
|
||||||
|
t->pointer.of = type_parametrize(t->pointer.of, scope);
|
||||||
|
} else if(t->type == TYPE_TYPE_ARRAY) {
|
||||||
|
t = type_shallow_copy(t);
|
||||||
|
|
||||||
|
t->array.of = type_parametrize(t->array.of, scope);
|
||||||
|
|
||||||
|
if(t->array.lengthIsGeneric) {
|
||||||
|
ScopeItem *vte = scope_find_int(scope, t->array.lengthGenericParamIdx, "i");
|
||||||
|
|
||||||
|
if(vte) {
|
||||||
|
assert(vte->kind == SCOPEITEM_CEXPR);
|
||||||
|
|
||||||
|
AST *n = vte->data.cexpr.concrete;
|
||||||
|
|
||||||
|
while(n->nodeKind == AST_EXPR_VAR && n->exprVar.thing->kind == SCOPEITEM_CEXPR && n->exprVar.thing->data.cexpr.concrete != NULL) {
|
||||||
|
n = n->exprVar.thing->data.cexpr.concrete;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(n->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
t->array.length = n->exprPrim.val;
|
||||||
|
t->array.lengthIsGeneric = false;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_VAR && n->exprVar.thing->kind == SCOPEITEM_CEXPR) {
|
||||||
|
t->array.lengthGenericParamIdx = n->exprVar.thing->data.cexpr.paramIdx;
|
||||||
|
t->array.lengthGenericParamName = n->exprVar.thing->data.cexpr.paramName;
|
||||||
|
t->array.lengthIsGeneric = true;
|
||||||
|
} else {
|
||||||
|
stahp_node(n, "Invalid parametrization expression.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *type_shallow_copy(Type *t) {
|
||||||
|
if(t->type == TYPE_TYPE_PRIMITIVE) {
|
||||||
|
Type *n = calloc(1, sizeof(TypePrimitive));
|
||||||
|
memcpy(n, t, sizeof(TypePrimitive));
|
||||||
|
return n;
|
||||||
|
} else if(t->type == TYPE_TYPE_POINTER) {
|
||||||
|
Type *n = calloc(1, sizeof(TypePointer));
|
||||||
|
memcpy(n, t, sizeof(TypePointer));
|
||||||
|
return n;
|
||||||
|
} else if(t->type == TYPE_TYPE_ARRAY) {
|
||||||
|
Type *n = calloc(1, sizeof(TypeArray));
|
||||||
|
memcpy(n, t, sizeof(TypeArray));
|
||||||
|
return n;
|
||||||
|
} else if(t->type == TYPE_TYPE_GENERIC) {
|
||||||
|
Type *n = calloc(1, sizeof(TypeGeneric));
|
||||||
|
memcpy(n, t, sizeof(TypeGeneric));
|
||||||
|
return n;
|
||||||
|
} else if(t->type == TYPE_TYPE_FUNCTION) {
|
||||||
|
Type *n = calloc(1, sizeof(TypeFunction));
|
||||||
|
n->type = TYPE_TYPE_FUNCTION;
|
||||||
|
|
||||||
|
n->function.ret = t->function.ret;
|
||||||
|
n->function.argCount = t->function.argCount;
|
||||||
|
|
||||||
|
n->function.argNames = calloc(n->function.argCount, sizeof(*n->function.argNames));
|
||||||
|
memcpy(n->function.argNames, t->function.argNames, n->function.argCount * sizeof(*n->function.argNames));
|
||||||
|
|
||||||
|
n->function.args = calloc(n->function.argCount, sizeof(*n->function.args));
|
||||||
|
memcpy(n->function.args, t->function.args, n->function.argCount * sizeof(*n->function.args));
|
||||||
|
|
||||||
|
return n;
|
||||||
|
} else if(t->type == TYPE_TYPE_RECORD) {
|
||||||
|
Type *n = calloc(1, sizeof(TypeRecord));
|
||||||
|
n->type = TYPE_TYPE_RECORD;
|
||||||
|
|
||||||
|
n->record.name = strdup(t->record.name);
|
||||||
|
|
||||||
|
n->record.fieldCount = t->record.fieldCount;
|
||||||
|
|
||||||
|
n->record.fieldNames = calloc(n->record.fieldCount, sizeof(*n->record.fieldNames));
|
||||||
|
memcpy(n->record.fieldNames, t->record.fieldNames, n->record.fieldCount * sizeof(*n->record.fieldNames));
|
||||||
|
|
||||||
|
n->record.fieldTypes = calloc(n->record.fieldCount, sizeof(*n->record.fieldTypes));
|
||||||
|
memcpy(n->record.fieldTypes, t->record.fieldTypes, n->record.fieldCount * sizeof(*n->record.fieldTypes));
|
||||||
|
|
||||||
|
n->record.fieldOffsets = calloc(n->record.fieldCount, sizeof(*n->record.fieldOffsets));
|
||||||
|
memcpy(n->record.fieldOffsets, t->record.fieldOffsets, n->record.fieldCount * sizeof(*n->record.fieldOffsets));
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *type_prim_cast(Type *t, size_t bits) {
|
||||||
|
assert(t->type == TYPE_TYPE_PRIMITIVE);
|
||||||
|
|
||||||
|
Type *t2 = type_shallow_copy(t);
|
||||||
|
t2->primitive.width = bits;
|
||||||
|
return t2;
|
||||||
|
}
|
||||||
|
|||||||
80
src/types.h
80
src/types.h
@@ -3,9 +3,14 @@
|
|||||||
|
|
||||||
#include<stddef.h>
|
#include<stddef.h>
|
||||||
#include<stdint.h>
|
#include<stdint.h>
|
||||||
|
#include<stdbool.h>
|
||||||
|
#include<string.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
#include<assert.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TYPE_TYPE_PRIMITIVE, TYPE_TYPE_COMPOUND, TYPE_TYPE_POINTER, TYPE_TYPE_FUNCTION, TYPE_TYPE_ARRAY, TYPE_TYPE_ERROR
|
TYPE_TYPE_PRIMITIVE, TYPE_TYPE_RECORD, TYPE_TYPE_POINTER, TYPE_TYPE_FUNCTION, TYPE_TYPE_ARRAY, TYPE_TYPE_GENERIC, TYPE_TYPE_ERROR
|
||||||
} TypeType;
|
} TypeType;
|
||||||
|
|
||||||
union Type;
|
union Type;
|
||||||
@@ -39,6 +44,7 @@ typedef struct TypeFunction {
|
|||||||
|
|
||||||
union Type *ret;
|
union Type *ret;
|
||||||
|
|
||||||
|
char **argNames;
|
||||||
union Type **args;
|
union Type **args;
|
||||||
size_t argCount;
|
size_t argCount;
|
||||||
} TypeFunction;
|
} TypeFunction;
|
||||||
@@ -47,9 +53,32 @@ typedef struct TypeArray {
|
|||||||
TypeType type;
|
TypeType type;
|
||||||
|
|
||||||
union Type *of;
|
union Type *of;
|
||||||
size_t length; /* 0 means unknown */
|
|
||||||
|
intmax_t length;
|
||||||
|
|
||||||
|
bool lengthIsGeneric;
|
||||||
|
char *lengthGenericParamName;
|
||||||
|
size_t lengthGenericParamIdx;
|
||||||
} TypeArray;
|
} TypeArray;
|
||||||
|
|
||||||
|
typedef struct TypeRecord {
|
||||||
|
TypeType type;
|
||||||
|
|
||||||
|
char *name;
|
||||||
|
|
||||||
|
union Type **fieldTypes;
|
||||||
|
size_t *fieldOffsets;
|
||||||
|
char **fieldNames;
|
||||||
|
size_t fieldCount;
|
||||||
|
} TypeRecord;
|
||||||
|
|
||||||
|
typedef struct TypeGeneric {
|
||||||
|
TypeType type;
|
||||||
|
|
||||||
|
char *paramName;
|
||||||
|
size_t paramIdx;
|
||||||
|
} TypeGeneric;
|
||||||
|
|
||||||
typedef union Type {
|
typedef union Type {
|
||||||
TypeType type;
|
TypeType type;
|
||||||
|
|
||||||
@@ -57,11 +86,14 @@ typedef union Type {
|
|||||||
TypePointer pointer;
|
TypePointer pointer;
|
||||||
TypeFunction function;
|
TypeFunction function;
|
||||||
TypeArray array;
|
TypeArray array;
|
||||||
|
TypeRecord record;
|
||||||
|
TypeGeneric generic;
|
||||||
} Type;
|
} Type;
|
||||||
|
|
||||||
extern Type TYPE_ERROR;
|
extern Type TYPE_ERROR;
|
||||||
|
|
||||||
Type *primitive_parse(const char*);
|
Type *primitive_parse(const char*);
|
||||||
|
Type *primitive_make(uint16_t width);
|
||||||
|
|
||||||
size_t type_size(Type*);
|
size_t type_size(Type*);
|
||||||
int type_equal(Type*, Type*);
|
int type_equal(Type*, Type*);
|
||||||
@@ -70,4 +102,48 @@ Type *type_pointer_wrap(Type*);
|
|||||||
/* 0 = not castable, 1 = explicitly castable, 2 = implicitly castable */
|
/* 0 = not castable, 1 = explicitly castable, 2 = implicitly castable */
|
||||||
int type_is_castable(Type *from, Type *to);
|
int type_is_castable(Type *from, Type *to);
|
||||||
|
|
||||||
|
int type_is_number(Type *t);
|
||||||
|
|
||||||
|
char *type_to_string(Type*);
|
||||||
|
|
||||||
|
struct Scope;
|
||||||
|
Type *type_parametrize(Type *target, struct Scope *scope);
|
||||||
|
|
||||||
|
Type *type_shallow_copy(Type *t);
|
||||||
|
|
||||||
|
bool type_is_generic(Type *t);
|
||||||
|
|
||||||
|
Type *type_prim_cast(Type *t, size_t bits);
|
||||||
|
|
||||||
|
static inline bool type_is_segmented_pointer(Type *type) {
|
||||||
|
return type->type == TYPE_TYPE_RECORD && !!strstr(type->record.name, " @far*");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool type_is_any_pointer(Type *type) {
|
||||||
|
return type->type == TYPE_TYPE_POINTER || type_is_segmented_pointer(type);
|
||||||
|
}
|
||||||
|
static inline Type *type_dereference(Type *type) {
|
||||||
|
assert(type_is_any_pointer(type));
|
||||||
|
|
||||||
|
if(type->type == TYPE_TYPE_POINTER) {
|
||||||
|
return type->pointer.of;
|
||||||
|
} else if(type_is_segmented_pointer(type)) {
|
||||||
|
return type->record.fieldTypes[1]->pointer.of;
|
||||||
|
}
|
||||||
|
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Type *type_u(size_t bits) {
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof(buf), "u%lu", bits);
|
||||||
|
return primitive_parse(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Type *type_s(size_t bits) {
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof(buf), "s%lu", bits);
|
||||||
|
return primitive_parse(buf);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
48
src/utils.h
48
src/utils.h
@@ -2,6 +2,12 @@
|
|||||||
#define NCTREF_UTILS_H
|
#define NCTREF_UTILS_H
|
||||||
|
|
||||||
#include<stddef.h>
|
#include<stddef.h>
|
||||||
|
#include<stdbool.h>
|
||||||
|
#include<errno.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<stdarg.h>
|
||||||
|
#include<stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
inline static size_t djb2(const char *str) {
|
inline static size_t djb2(const char *str) {
|
||||||
size_t hash = 5381;
|
size_t hash = 5381;
|
||||||
@@ -14,4 +20,46 @@ inline static size_t djb2(const char *str) {
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline static bool unstupid_strtol(const char *str, char **endptr, int base, long *result) {
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
char *endptr2 = NULL;
|
||||||
|
*result = strtol(str, &endptr2, base);
|
||||||
|
|
||||||
|
if(endptr2 == str) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(errno == ERANGE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(endptr) {
|
||||||
|
*endptr = endptr2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((format(printf, 1, 2))) static inline char *malp(const char *fmt, ...) {
|
||||||
|
va_list v1, v2;
|
||||||
|
va_start(v1, fmt);
|
||||||
|
va_copy(v2, v1);
|
||||||
|
size_t len = vsnprintf(NULL, 0, fmt, v1);
|
||||||
|
va_end(v1);
|
||||||
|
va_start(v2, fmt);
|
||||||
|
char *str = malloc(len + 1);
|
||||||
|
vsnprintf(str, len + 1, fmt, v2);
|
||||||
|
str[len] = 0;
|
||||||
|
va_end(v2);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int is_str_equal_check_null(const char *s1, const char *s2) {
|
||||||
|
if (!s1 || !s2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return !strcmp(s1, s2);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
#include"vartable.h"
|
|
||||||
|
|
||||||
#include"utils.h"
|
|
||||||
#include<stdlib.h>
|
|
||||||
#include<string.h>
|
|
||||||
|
|
||||||
struct ReachingDefs *reachingdefs_push(struct ReachingDefs *this) {
|
|
||||||
struct ReachingDefs *ret = calloc(1, sizeof(*ret));
|
|
||||||
ret->parent = this;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ReachingDefs *reachingdefs_coalesce(struct ReachingDefs *this) {
|
|
||||||
struct ReachingDefs *parent = this->parent;
|
|
||||||
if(parent) {
|
|
||||||
parent->defs = realloc(parent->defs, sizeof(*parent->defs) * (parent->defCount + this->defCount));
|
|
||||||
memcpy(&parent->defs[parent->defCount], this->defs, sizeof(*this->defs) * this->defCount);
|
|
||||||
parent->defCount += this->defCount;
|
|
||||||
}
|
|
||||||
free(this->defs);
|
|
||||||
free(this);
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reachingdefs_set(struct ReachingDefs *this, union AST *def) {
|
|
||||||
this->defCount = 1;
|
|
||||||
this->defs = realloc(this->defs, sizeof(*this->defs) * this->defCount);
|
|
||||||
this->defs[0] = def;
|
|
||||||
}
|
|
||||||
|
|
||||||
VarTable *vartable_new(VarTable *parent) {
|
|
||||||
VarTable *ret = malloc(sizeof(*ret));
|
|
||||||
ret->parent = parent;
|
|
||||||
ret->count = 0;
|
|
||||||
ret->names = NULL;
|
|
||||||
ret->data = NULL;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
VarTableEntry *vartable_get(VarTable *this, const char *name) {
|
|
||||||
for(size_t v = 0; v < this->count; v++) {
|
|
||||||
if(!strcmp(name, this->names[v])) return this->data[v];
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
VarTableEntry *vartable_find(VarTable *this, const char *name) {
|
|
||||||
VarTable *tbl = this;
|
|
||||||
while(tbl) {
|
|
||||||
VarTableEntry *entry = vartable_get(tbl, name);
|
|
||||||
if(entry) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
tbl = tbl->parent;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
VarTableEntry *vartable_set(VarTable *this, const char *name, VarTableEntry *e) {
|
|
||||||
this->names = realloc(this->names, sizeof(*this->names) * (this->count + 1));
|
|
||||||
this->data = realloc(this->data, sizeof(*this->data) * (this->count + 1));
|
|
||||||
this->names[this->count] = name;
|
|
||||||
this->data[this->count] = e;
|
|
||||||
this->count++;
|
|
||||||
if(e->kind == VARTABLEENTRY_VAR) e->data.var.name = name;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
void vartable_new_reachingdefs_for_all_vars(VarTable *this) {
|
|
||||||
for(size_t i = 0; i < this->count; i++) {
|
|
||||||
if(this->data[i]->kind == VARTABLEENTRY_VAR) {
|
|
||||||
this->data[i]->data.var.reachingDefs = reachingdefs_push(this->data[i]->data.var.reachingDefs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this->parent) {
|
|
||||||
vartable_new_reachingdefs_for_all_vars(this->parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void vartable_coalesce_reachingdefs_for_all_vars(VarTable *this) {
|
|
||||||
for(size_t i = 0; i < this->count; i++) {
|
|
||||||
if(this->data[i]->kind == VARTABLEENTRY_VAR) {
|
|
||||||
this->data[i]->data.var.reachingDefs = reachingdefs_coalesce(this->data[i]->data.var.reachingDefs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this->parent) {
|
|
||||||
vartable_coalesce_reachingdefs_for_all_vars(this->parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#ifndef NCTREF_VARTABLE_H
|
|
||||||
#define NCTREF_VARTABLE_H
|
|
||||||
|
|
||||||
#include"types.h"
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
VARTABLEENTRY_SYMBOL, VARTABLEENTRY_TYPE, VARTABLEENTRY_VAR
|
|
||||||
} VarTableEntryKind;
|
|
||||||
|
|
||||||
union AST;
|
|
||||||
typedef struct UseDef {
|
|
||||||
union AST *def; // assign stmt
|
|
||||||
union AST *use; // corresponding AST_EXPR_VAR
|
|
||||||
union AST *stmt; // whole using stmt
|
|
||||||
struct UseDef *next;
|
|
||||||
} UseDef;
|
|
||||||
|
|
||||||
// Stack, necessary for "possible reaching defs" such as from if statements
|
|
||||||
typedef struct ReachingDefs {
|
|
||||||
size_t defCount;
|
|
||||||
union AST **defs;
|
|
||||||
|
|
||||||
struct ReachingDefs *parent;
|
|
||||||
} ReachingDefs;
|
|
||||||
struct ReachingDefs *reachingdefs_push(struct ReachingDefs*);
|
|
||||||
struct ReachingDefs *reachingdefs_coalesce(struct ReachingDefs*);
|
|
||||||
void reachingdefs_set(struct ReachingDefs*, union AST*);
|
|
||||||
|
|
||||||
typedef struct VarTableEntry {
|
|
||||||
Type *type;
|
|
||||||
|
|
||||||
VarTableEntryKind kind;
|
|
||||||
struct {
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
char isLocal;
|
|
||||||
char isExternal;
|
|
||||||
const char *name;
|
|
||||||
} symbol;
|
|
||||||
struct {
|
|
||||||
// For debugging
|
|
||||||
|
|
||||||
const char *name;
|
|
||||||
|
|
||||||
// Register allocation
|
|
||||||
|
|
||||||
// vars in loops have higher priority
|
|
||||||
// a more advanced approach would be to use weights for different colors (e.g. vars for mul "should" be in eax)
|
|
||||||
uint8_t priority;
|
|
||||||
uint16_t color, degree;
|
|
||||||
|
|
||||||
// Used during parsing
|
|
||||||
|
|
||||||
ReachingDefs *reachingDefs;
|
|
||||||
|
|
||||||
// Optimizations
|
|
||||||
|
|
||||||
UseDef *usedefFirst;
|
|
||||||
UseDef *usedefLast;
|
|
||||||
} var;
|
|
||||||
};
|
|
||||||
} data;
|
|
||||||
} VarTableEntry;
|
|
||||||
|
|
||||||
typedef struct VarTable {
|
|
||||||
struct VarTable *parent;
|
|
||||||
|
|
||||||
size_t count;
|
|
||||||
const char **names;
|
|
||||||
VarTableEntry **data;
|
|
||||||
} VarTable;
|
|
||||||
|
|
||||||
VarTable *vartable_new(VarTable*);
|
|
||||||
VarTableEntry *vartable_get(VarTable*, const char*);
|
|
||||||
VarTableEntry *vartable_find(VarTable*, const char*);
|
|
||||||
VarTableEntry *vartable_set(VarTable*, const char*, VarTableEntry*);
|
|
||||||
|
|
||||||
void vartable_new_reachingdefs_for_all_vars(VarTable*);
|
|
||||||
void vartable_coalesce_reachingdefs_for_all_vars(VarTable*);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
62
src/x86/arch.c
Normal file
62
src/x86/arch.c
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#include"arch.h"
|
||||||
|
|
||||||
|
RegisterClass REG_CLASSES[] = {
|
||||||
|
[REG_CLASS_8] = {
|
||||||
|
.rMask = HWR_GPR, .w = 1, .p = 8,
|
||||||
|
.rs = {HWR_AL, HWR_AH, HWR_BL, HWR_BH, HWR_CL, HWR_CH, HWR_DL, HWR_DH},
|
||||||
|
.rsN = {"al", "ah", "bl", "bh", "cl", "ch", "dl", "dh"},
|
||||||
|
.rsS = {1, 1, 1, 1, 1, 1, 1, 1},
|
||||||
|
},
|
||||||
|
[REG_CLASS_NOT_8] = {
|
||||||
|
.rMask = HWR_GPR, .w = 2, .p = 4,
|
||||||
|
.rs = {HWR_AX, HWR_EAX, HWR_BX, HWR_EBX, HWR_CX, HWR_ECX, HWR_DX, HWR_EDX, HWR_DI, HWR_EDI, HWR_SI, HWR_ESI, HWR_BP, HWR_EBP},
|
||||||
|
.rsN = {"ax", "eax", "bx", "ebx", "cx", "ecx", "dx", "edx", "di", "edi", "si", "esi", "bp", "ebp"},
|
||||||
|
.rsS = {2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4},
|
||||||
|
},
|
||||||
|
[REG_CLASS_16_32] = {
|
||||||
|
.rMask = HWR_GPR, .w = 2, .p = 4,
|
||||||
|
.rs = {HWR_AX, HWR_EAX, HWR_BX, HWR_EBX, HWR_CX, HWR_ECX, HWR_DX, HWR_EDX, HWR_BP, HWR_EBP},
|
||||||
|
.rsN = {"ax", "eax", "bx", "ebx", "cx", "ecx", "dx", "edx", "bp", "ebp"},
|
||||||
|
.rsS = {2, 4, 2, 4, 2, 4, 2, 4, 2, 4},
|
||||||
|
},
|
||||||
|
[REG_CLASS_IND] = {
|
||||||
|
.rMask = HWR_IND, .w = 2, .p = 2,
|
||||||
|
.rs = {HWR_DI, HWR_EDI, HWR_SI, HWR_ESI},
|
||||||
|
.rsN = {"di", "edi", "si", "esi"},
|
||||||
|
.rsS = {2, 4, 2, 4},
|
||||||
|
},
|
||||||
|
[REG_CLASS_IA16_PTRS] = {
|
||||||
|
.rMask = HWR_IND | HWR_BX,
|
||||||
|
.rs = {HWR_DI, HWR_SI, HWR_BX},
|
||||||
|
.rsN = {"di", "si", "bx"},
|
||||||
|
.rsS = {2, 2, 2},
|
||||||
|
},
|
||||||
|
[REG_CLASS_IA16_USEABLE]= {
|
||||||
|
.rMask = HWR_IND | HWR_GPR,
|
||||||
|
.rs = {HWR_AX, HWR_CX, HWR_DX, HWR_DI, HWR_SI, HWR_BX},
|
||||||
|
.rsN = {"ax", "cx", "dx", "di", "si", "bx"},
|
||||||
|
.rsS = {2, 2, 2, 2, 2, 2},
|
||||||
|
},
|
||||||
|
[REG_CLASS_DATASEGS] = {
|
||||||
|
.rMask = HWR_SEGREGS,
|
||||||
|
.rs = {HWR_DS, HWR_ES, HWR_FS, HWR_GS},
|
||||||
|
.rsN = {"ds", "es", "fs", "gs"},
|
||||||
|
.rsS = {2, 2, 2, 2},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
bool arch_verify_target() {
|
||||||
|
if(x86_target() == IUNKNOWN86) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int arch_ptr_size() {
|
||||||
|
return x86_ia16() ? 2 : 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arch_init() {
|
||||||
|
|
||||||
|
}
|
||||||
364
src/x86/arch.h
Normal file
364
src/x86/arch.h
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include<string.h>
|
||||||
|
#include"ntc.h"
|
||||||
|
#include<assert.h>
|
||||||
|
#include<stdbool.h>
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include"ast/ast.h"
|
||||||
|
|
||||||
|
#define HWR_AL 1
|
||||||
|
#define HWR_AH 2
|
||||||
|
#define HWR_BL 4
|
||||||
|
#define HWR_BH 8
|
||||||
|
#define HWR_CL 16
|
||||||
|
#define HWR_CH 32
|
||||||
|
#define HWR_DL 64
|
||||||
|
#define HWR_DH 128
|
||||||
|
#define HWR_DI 256
|
||||||
|
#define HWR_SI 512
|
||||||
|
#define HWR_BP 1024
|
||||||
|
|
||||||
|
#define HWR_DS 2048
|
||||||
|
#define HWR_ES 4096
|
||||||
|
#define HWR_FS 8192
|
||||||
|
#define HWR_GS 16384
|
||||||
|
|
||||||
|
#define HWR_AX (HWR_AL | HWR_AH)
|
||||||
|
#define HWR_BX (HWR_BL | HWR_BH)
|
||||||
|
#define HWR_CX (HWR_CL | HWR_CH)
|
||||||
|
#define HWR_DX (HWR_DL | HWR_DH)
|
||||||
|
|
||||||
|
#define HWR_EAX HWR_AX
|
||||||
|
#define HWR_EBX HWR_BX
|
||||||
|
#define HWR_ECX HWR_CX
|
||||||
|
#define HWR_EDX HWR_DX
|
||||||
|
|
||||||
|
#define HWR_EDI HWR_DI
|
||||||
|
#define HWR_ESI HWR_SI
|
||||||
|
#define HWR_EBP HWR_BP
|
||||||
|
|
||||||
|
#define HWR_GPR (HWR_EAX | HWR_EBX | HWR_ECX | HWR_EDX | HWR_EDI | HWR_ESI | HWR_EBP)
|
||||||
|
#define HWR_IND (HWR_EDI | HWR_ESI)
|
||||||
|
#define HWR_ALL (HWR_GPR | HWR_IND)
|
||||||
|
|
||||||
|
#define HWR_SEGREGS (HWR_DS | HWR_ES | HWR_FS | HWR_GS)
|
||||||
|
|
||||||
|
#define MAX_REGS_PER_CLASS 16
|
||||||
|
typedef struct RegisterClass {
|
||||||
|
uint32_t rMask;
|
||||||
|
uint16_t w;
|
||||||
|
uint16_t p;
|
||||||
|
|
||||||
|
uint32_t rs[MAX_REGS_PER_CLASS];
|
||||||
|
const char *rsN[MAX_REGS_PER_CLASS];
|
||||||
|
uint8_t rsS[MAX_REGS_PER_CLASS];
|
||||||
|
} RegisterClass;
|
||||||
|
|
||||||
|
#define REG_CLASS_8 0
|
||||||
|
#define REG_CLASS_NOT_8 1
|
||||||
|
#define REG_CLASS_16_32 2
|
||||||
|
#define REG_CLASS_IND 3
|
||||||
|
#define REG_CLASS_IA16_PTRS 4
|
||||||
|
#define REG_CLASS_IA16_USEABLE 5
|
||||||
|
#define REG_CLASS_DATASEGS 6
|
||||||
|
extern RegisterClass REG_CLASSES[];
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
I086 = 0,
|
||||||
|
I186 = 1,
|
||||||
|
I286 = 2,
|
||||||
|
I386 = 3,
|
||||||
|
IUNKNOWN86 = 255,
|
||||||
|
} X86Target;
|
||||||
|
|
||||||
|
static inline X86Target x86_target() {
|
||||||
|
const char *str = ntc_get_arg("target");
|
||||||
|
|
||||||
|
if(!str) return I386;
|
||||||
|
|
||||||
|
if(!strcmp(str, "086")) {
|
||||||
|
return I086;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!strcmp(str, "186")) {
|
||||||
|
return I186;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!strcmp(str, "286")) {
|
||||||
|
return I286;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!strcmp(str, "386")) {
|
||||||
|
return I386;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IUNKNOWN86;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool x86_ia16() {
|
||||||
|
return x86_target() < I386;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t x86_max_gpr_size() {
|
||||||
|
switch(x86_target()) {
|
||||||
|
case I086:
|
||||||
|
case I186:
|
||||||
|
case I286:
|
||||||
|
return 2;
|
||||||
|
case I386:
|
||||||
|
return 4;
|
||||||
|
default: abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool x86_imul_supported() {
|
||||||
|
return x86_target() >= I186;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool x86_is_marked_ptr(ScopeItem *si) {
|
||||||
|
return si->data.var.preclassed && si->data.var.registerClass == REG_CLASS_IA16_PTRS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can expression be expressed as a single x86 operand?
|
||||||
|
#define XOP_NOT_XOP 0
|
||||||
|
#define XOP_NOT_MEM 1
|
||||||
|
#define XOP_MEM 2
|
||||||
|
static inline int is_xop(AST *e) {
|
||||||
|
if(e->nodeKind == AST_EXPR_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER) {
|
||||||
|
e = e->exprCast.what;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
return XOP_NOT_MEM;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_VAR) {
|
||||||
|
return e->exprVar.thing->kind == SCOPEITEM_VAR ? XOP_NOT_MEM : XOP_MEM;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_DEREF && e->exprBinOp.operands[0]->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operands[0]->exprBinOp.operator == BINOP_ADD && is_xop(e->exprBinOp.operands[0]->exprBinOp.operands[0]) == XOP_NOT_MEM && is_xop(e->exprBinOp.operands[0]->exprBinOp.operands[1]) == XOP_NOT_MEM) {
|
||||||
|
return XOP_MEM;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == SCOPEITEM_SYMBOL) {
|
||||||
|
return XOP_NOT_MEM;
|
||||||
|
} else if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_DEREF) {
|
||||||
|
AST *c = e->exprBinOp.operands[0];
|
||||||
|
|
||||||
|
if(c->nodeKind == AST_EXPR_CAST && c->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && c->exprCast.to->type == TYPE_TYPE_POINTER) {
|
||||||
|
c = c->exprCast.what;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
return XOP_MEM;
|
||||||
|
} else if(c->nodeKind == AST_EXPR_VAR && c->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||||
|
if(x86_ia16() && !x86_is_marked_ptr(c->exprVar.thing)) {
|
||||||
|
// In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
|
||||||
|
return XOP_NOT_XOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return XOP_MEM;
|
||||||
|
} else if(
|
||||||
|
(c->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operator == BINOP_ADD && c->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && c->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && c->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR) ||
|
||||||
|
(c->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operator == BINOP_ADD && c->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR)) {
|
||||||
|
|
||||||
|
if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
return XOP_MEM;
|
||||||
|
} else if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR) {
|
||||||
|
if(x86_ia16() && !x86_is_marked_ptr(c->exprBinOp.operands[1]->exprVar.thing)) {
|
||||||
|
// In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
|
||||||
|
return XOP_NOT_XOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return XOP_MEM;
|
||||||
|
} else if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && c->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && c->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR) {
|
||||||
|
if(x86_ia16() && !x86_is_marked_ptr(c->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing)) {
|
||||||
|
// In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
|
||||||
|
return XOP_NOT_XOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scale = c->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val;
|
||||||
|
|
||||||
|
if(scale == 1 || scale == 2 || scale == 4 || scale == 8) {
|
||||||
|
return XOP_MEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(e->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
return XOP_NOT_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return XOP_NOT_XOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NOT_AT_ALL_IT,
|
||||||
|
DEST_IS_BAD_XOP,
|
||||||
|
SRC0_IS_BAD_XOP,
|
||||||
|
SRC1_IS_BAD_XOP,
|
||||||
|
DEST_NOT_PRECOLORED,
|
||||||
|
SRC0_NOT_PRECOLORED,
|
||||||
|
SRC1_NOT_PRECOLORED,
|
||||||
|
DEST_NOT_SRC0,
|
||||||
|
MISSING_MULHI_FOR_MUL,
|
||||||
|
GUCCI,
|
||||||
|
} WhysItBad_Huh;
|
||||||
|
|
||||||
|
static inline WhysItBad_Huh x86_test_mul_matching_mulhi(AST *mulhi, AST *mul) {
|
||||||
|
assert(mulhi->statement.next == mul);
|
||||||
|
|
||||||
|
if(mulhi->stmtAssign.to->nodeKind != AST_EXPR_BINARY_OP) {
|
||||||
|
return NOT_AT_ALL_IT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mulhi->stmtAssign.to->exprBinOp.operator != BINOP_MULHI) {
|
||||||
|
return NOT_AT_ALL_IT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ast_expression_equal(mulhi->stmtAssign.to->exprBinOp.operands[0], mul->stmtAssign.to->exprBinOp.operands[0])) {
|
||||||
|
return NOT_AT_ALL_IT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ast_expression_equal(mulhi->stmtAssign.to->exprBinOp.operands[1], mul->stmtAssign.to->exprBinOp.operands[1])) {
|
||||||
|
return NOT_AT_ALL_IT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_xop(mulhi->stmtAssign.what) != XOP_NOT_MEM) {
|
||||||
|
return DEST_IS_BAD_XOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GUCCI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline WhysItBad_Huh x86_test_mul(AST *stmtPrev, AST *stmt) {
|
||||||
|
if(stmt->nodeKind != AST_STMT_ASSIGN) {
|
||||||
|
return NOT_AT_ALL_IT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stmt->stmtAssign.to->nodeKind != AST_EXPR_BINARY_OP) {
|
||||||
|
return NOT_AT_ALL_IT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stmt->stmtAssign.to->exprBinOp.operator != BINOP_MUL) {
|
||||||
|
return NOT_AT_ALL_IT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stmt->stmtAssign.what->nodeKind != AST_EXPR_VAR || stmt->stmtAssign.what->exprVar.thing->kind != SCOPEITEM_VAR) {
|
||||||
|
return DEST_IS_BAD_XOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(x86_imul_supported()) {
|
||||||
|
if(ast_expression_equal(stmt->stmtAssign.what, stmt->stmtAssign.to->exprBinOp.operands[0])) {
|
||||||
|
if(is_xop(stmt->stmtAssign.to->exprBinOp.operands[1]) == XOP_NOT_XOP) {
|
||||||
|
return SRC1_IS_BAD_XOP;
|
||||||
|
}
|
||||||
|
return GUCCI;
|
||||||
|
} else {
|
||||||
|
if(is_xop(stmt->stmtAssign.to->exprBinOp.operands[0]) == XOP_NOT_XOP) {
|
||||||
|
return SRC0_IS_BAD_XOP;
|
||||||
|
}
|
||||||
|
if(stmt->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
return GUCCI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(stmt->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
return SRC1_IS_BAD_XOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_xop(stmt->stmtAssign.to->exprBinOp.operands[1]) == XOP_NOT_XOP) {
|
||||||
|
return SRC1_IS_BAD_XOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if(stmt->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && x86_get_target() < I186) {
|
||||||
|
return SRC1_IS_BAD_XOP;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if(!ast_expression_equal(stmt->stmtAssign.what, stmt->stmtAssign.to->exprBinOp.operands[0])) {
|
||||||
|
return DEST_NOT_SRC0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!stmt->stmtAssign.what->exprVar.thing->data.var.precolored) {
|
||||||
|
return DEST_NOT_PRECOLORED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(x86_test_mul_matching_mulhi(stmtPrev, stmt) != GUCCI) {
|
||||||
|
return MISSING_MULHI_FOR_MUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GUCCI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void arch_add_hidden_variables(Scope *scope) {
|
||||||
|
/*if(!scope_find(scope, "@seg_ds")) {
|
||||||
|
ScopeItem *si = calloc(1, sizeof(*si));
|
||||||
|
si->type = primitive_parse("u16");
|
||||||
|
si->kind = SCOPEITEM_VAR;
|
||||||
|
si->data.var.preclassed = true;
|
||||||
|
si->data.var.registerClass = REG_CLASS_DATASEGS;
|
||||||
|
si->data.var.precolored = true;
|
||||||
|
si->data.var.color = 0;
|
||||||
|
scope_set(scope, "@seg_ds", si);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool x86_is_lea(AST *s) {
|
||||||
|
return !x86_ia16() && s->nodeKind == AST_STMT_ASSIGN && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && is_xop(s->stmtAssign.what) == XOP_NOT_MEM && is_xop(s->stmtAssign.to->exprBinOp.operands[0]) == XOP_NOT_MEM && is_xop(s->stmtAssign.to->exprBinOp.operands[1]) == XOP_NOT_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int arch_sp_size() {
|
||||||
|
return x86_max_gpr_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int arch_gpr_size() {
|
||||||
|
return x86_max_gpr_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int arch_memory_width() {
|
||||||
|
switch(x86_target()) {
|
||||||
|
case I086:
|
||||||
|
case I186:
|
||||||
|
return 20;
|
||||||
|
case I286:
|
||||||
|
return 24;
|
||||||
|
default:
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lol
|
||||||
|
static inline bool is_reg_b(int cls, int res) {
|
||||||
|
const char *name = REG_CLASSES[cls].rsN[res];
|
||||||
|
return !strcmp(name, "bl") || !strcmp(name, "bh") || !!strstr(name, "bx");
|
||||||
|
}
|
||||||
|
static inline bool is_reg_di(int cls, int res) {
|
||||||
|
const char *name = REG_CLASSES[cls].rsN[res];
|
||||||
|
return !!strstr(name, "di");
|
||||||
|
}
|
||||||
|
static inline bool is_reg_si(int cls, int res) {
|
||||||
|
const char *name = REG_CLASSES[cls].rsN[res];
|
||||||
|
return !!strstr(name, "si");
|
||||||
|
}
|
||||||
|
static inline void reg_cast_to_gpr(int *cls, int *res) {
|
||||||
|
const char *name = REG_CLASSES[*cls].rsN[*res];
|
||||||
|
if(strstr(name, "a")) {
|
||||||
|
*cls = REG_CLASS_16_32;
|
||||||
|
*res = 1;
|
||||||
|
} else if(strstr(name, "b")) {
|
||||||
|
*cls = REG_CLASS_16_32;
|
||||||
|
*res = 3;
|
||||||
|
} else if(strstr(name, "c")) {
|
||||||
|
*cls = REG_CLASS_16_32;
|
||||||
|
*res = 5;
|
||||||
|
} else if(strstr(name, "dl") || strstr(name, "dh") || strstr(name, "dx")) {
|
||||||
|
*cls = REG_CLASS_16_32;
|
||||||
|
*res = 7;
|
||||||
|
} else if(strstr(name, "di")) {
|
||||||
|
*cls = REG_CLASS_16_32;
|
||||||
|
*res = 9;
|
||||||
|
} else if(strstr(name, "si")) {
|
||||||
|
*cls = REG_CLASS_16_32;
|
||||||
|
*res = 11;
|
||||||
|
}
|
||||||
|
if(x86_ia16()) {
|
||||||
|
(*res)--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void arch_init();
|
||||||
942
src/x86/cg.c
Normal file
942
src/x86/cg.c
Normal file
@@ -0,0 +1,942 @@
|
|||||||
|
#include<stdlib.h>
|
||||||
|
#include<signal.h>
|
||||||
|
#include<string.h>
|
||||||
|
#include<assert.h>
|
||||||
|
#include"ntc.h"
|
||||||
|
#include"reporting.h"
|
||||||
|
#include"ast/ast.h"
|
||||||
|
#include"arch.h"
|
||||||
|
#include"utils.h"
|
||||||
|
|
||||||
|
static const char *BINOP_SIMPLE_INSTRS[] = {[BINOP_ADD] = "add", [BINOP_SUB] = "sub", [BINOP_BITWISE_AND] = "and", [BINOP_BITWISE_OR] = "or", [BINOP_BITWISE_XOR] = "xor"};
|
||||||
|
|
||||||
|
/*static size_t nextLocalLabel = 0;*/
|
||||||
|
|
||||||
|
struct CalleeSavedState {
|
||||||
|
const char *reg[MAX_REGS_PER_CLASS];
|
||||||
|
size_t stackOffset[MAX_REGS_PER_CLASS];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/*#define LOOPSTACKSIZE 96
|
||||||
|
size_t loopStackStart[LOOPSTACKSIZE];
|
||||||
|
size_t loopStackEnd[LOOPSTACKSIZE];
|
||||||
|
size_t loopStackIdx;*/
|
||||||
|
|
||||||
|
int isFunction;
|
||||||
|
|
||||||
|
AST *tlc;
|
||||||
|
|
||||||
|
struct CalleeSavedState calleeSaved;
|
||||||
|
} CGState;
|
||||||
|
|
||||||
|
static const char *direct(int size) {
|
||||||
|
switch(size) {
|
||||||
|
case 0:
|
||||||
|
case 1: return "db";
|
||||||
|
case 2: return "dw";
|
||||||
|
case 4: return "dd";
|
||||||
|
case 8: return "dq";
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *spec(int size) {
|
||||||
|
switch(size) {
|
||||||
|
case 0:
|
||||||
|
case 1: return "byte";
|
||||||
|
case 2: return "word";
|
||||||
|
case 4: return "dword";
|
||||||
|
case 8: return "qword";
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static int log_size(int size) {
|
||||||
|
switch(size) {
|
||||||
|
case 0:
|
||||||
|
case 1: return 0;
|
||||||
|
case 2: return 1;
|
||||||
|
case 4: return 2;
|
||||||
|
case 8: return 3;
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *specexpr(AST *e) {
|
||||||
|
return spec(type_size(e->expression.type));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
static const char *xv_sz(ScopeItem *v, int sz) {
|
||||||
|
assert(v->kind == SCOPEITEM_VAR);
|
||||||
|
|
||||||
|
if(sz == 0) sz = arch_ptr_size();
|
||||||
|
|
||||||
|
#define XVBUFS 8
|
||||||
|
#define XVBUFSZ 16
|
||||||
|
static char bufs[XVBUFS][XVBUFSZ];
|
||||||
|
static int bufidx = 0;
|
||||||
|
|
||||||
|
char *ret = bufs[bufidx];
|
||||||
|
|
||||||
|
if(ntc_get_int("rcd")) {
|
||||||
|
snprintf(ret, XVBUFSZ, "%i@%i", v->data.var.registerClass, v->data.var.color);
|
||||||
|
} else {
|
||||||
|
int cls = v->data.var.registerClass, reg = v->data.var.color;
|
||||||
|
|
||||||
|
if(type_size(v->type) != sz) {
|
||||||
|
if(sz == x86_max_gpr_size()) {
|
||||||
|
reg_cast_to_gpr(&cls, ®);
|
||||||
|
} else abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(ret, XVBUFSZ, "%s", REG_CLASSES[cls].rsN[reg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bufidx = (bufidx + 1) % XVBUFS;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *xv(ScopeItem *v) {
|
||||||
|
return xv_sz(v, type_size(v->type));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *xj(BinaryOp op) {
|
||||||
|
switch(op) {
|
||||||
|
case BINOP_EQUAL: return "e";
|
||||||
|
case BINOP_NEQUAL: return "ne";
|
||||||
|
case BINOP_LESS: return "b";
|
||||||
|
case BINOP_GREATER: return "a";
|
||||||
|
case BINOP_LEQUAL: return "be";
|
||||||
|
case BINOP_GEQUAL: return "ae";
|
||||||
|
default: abort(); return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AST *is_field_access(AST *e) {
|
||||||
|
if(e->nodeKind != AST_EXPR_BINARY_OP || e->exprUnOp.operator != BINOP_DEREF) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = e->exprBinOp.operands[0];
|
||||||
|
|
||||||
|
if(e->nodeKind == AST_EXPR_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER) {
|
||||||
|
e = e->exprCast.what;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_ADD && e->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && e->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == SCOPEITEM_SYMBOL) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_ADD && e->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && e->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && e->exprBinOp.operands[0]->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *segment_or_empty(AST *a) {
|
||||||
|
if(!a) {
|
||||||
|
return "ds";
|
||||||
|
}
|
||||||
|
assert(a->nodeKind == AST_EXPR_VAR);
|
||||||
|
assert(a->exprVar.thing->kind == SCOPEITEM_VAR);
|
||||||
|
assert(a->exprVar.thing->data.var.registerClass == REG_CLASS_DATASEGS);
|
||||||
|
return REG_CLASSES[REG_CLASS_DATASEGS].rsN[a->exprVar.thing->data.var.color];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a XOP-able expression into an x86 operand.
|
||||||
|
* Result MUST be determinstic and always the same, for the same given expression.
|
||||||
|
* */
|
||||||
|
static const char *xop_sz(AST *tlc, AST *e, int sz) {
|
||||||
|
#define XOPBUFS 16
|
||||||
|
#define XOPBUFSZ 64
|
||||||
|
static char bufs[XOPBUFS][XOPBUFSZ];
|
||||||
|
static int bufidx = 0;
|
||||||
|
|
||||||
|
char *ret = bufs[bufidx];
|
||||||
|
|
||||||
|
int pr = 0;
|
||||||
|
|
||||||
|
if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_DEREF) {
|
||||||
|
AST *seg = e->exprBinOp.operands[1];
|
||||||
|
AST *p = e->exprBinOp.operands[0];
|
||||||
|
|
||||||
|
if(p->nodeKind == AST_EXPR_CAST && p->exprCast.to->type == TYPE_TYPE_POINTER) {
|
||||||
|
p = p->exprCast.what;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprVar.thing->kind == SCOPEITEM_VAR && p->exprBinOp.operands[1]->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "%s %s:[%s + %s]",
|
||||||
|
spec(sz),
|
||||||
|
segment_or_empty(seg),
|
||||||
|
xv_sz(p->exprBinOp.operands[0]->exprVar.thing, 0),
|
||||||
|
xv_sz(p->exprBinOp.operands[1]->exprVar.thing, 0));
|
||||||
|
} else if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && p->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == SCOPEITEM_SYMBOL && p->exprBinOp.operands[1]->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "%s %s:[%s + %s]",
|
||||||
|
spec(sz),
|
||||||
|
segment_or_empty(seg),
|
||||||
|
p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
|
||||||
|
xv_sz(p->exprBinOp.operands[1]->exprVar.thing, 0));
|
||||||
|
} else if(is_field_access(e)) {
|
||||||
|
e = is_field_access(e);
|
||||||
|
|
||||||
|
if(e->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP) {
|
||||||
|
assert(e->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF);
|
||||||
|
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "%s %s:[%s + %i]",
|
||||||
|
spec(sz),
|
||||||
|
segment_or_empty(seg),
|
||||||
|
e->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
|
||||||
|
e->exprBinOp.operands[1]->exprPrim.val);
|
||||||
|
} else {
|
||||||
|
assert(e->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR);
|
||||||
|
|
||||||
|
ScopeItem *vte = e->exprBinOp.operands[0]->exprVar.thing;
|
||||||
|
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "%s %s:[%s + %i]",
|
||||||
|
spec(sz),
|
||||||
|
segment_or_empty(seg),
|
||||||
|
REG_CLASSES[vte->data.var.registerClass].rsN[vte->data.var.color],
|
||||||
|
e->exprBinOp.operands[1]->exprPrim.val);
|
||||||
|
}
|
||||||
|
} else if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && p->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == SCOPEITEM_SYMBOL && p->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && p->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && p->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && p->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "%s %s:[%s + %i * %s]",
|
||||||
|
spec(sz),
|
||||||
|
segment_or_empty(seg),
|
||||||
|
p->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
|
||||||
|
p->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val,
|
||||||
|
xv_sz(p->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing, 0));
|
||||||
|
} else if(p->nodeKind == AST_EXPR_VAR && p->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "%s %s:[%s]", spec(sz), segment_or_empty(seg), xv_sz(p->exprVar.thing, 0));
|
||||||
|
} else if(p->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
if(x86_ia16()) {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "[bp + %li]", 0 - tlc->chunk.stackReservation);
|
||||||
|
} else {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "[esp + %i]", 0);
|
||||||
|
}
|
||||||
|
} else if(p->nodeKind == AST_EXPR_BINARY_OP && p->exprBinOp.operator == BINOP_ADD && p->exprBinOp.operands[0]->nodeKind == AST_EXPR_STACK_POINTER && p->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
if(x86_ia16()) {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "[bp + %li]", p->exprBinOp.operands[1]->exprPrim.val - tlc->chunk.stackReservation);
|
||||||
|
} else {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "[esp + %i]", p->exprBinOp.operands[1]->exprPrim.val);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(e->nodeKind == AST_EXPR_STACK_POINTER) {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, x86_ia16() ? "sp" : "esp");
|
||||||
|
} else if(e->nodeKind == AST_EXPR_VAR) {
|
||||||
|
ScopeItem *v = e->exprVar.thing;
|
||||||
|
|
||||||
|
if(v->kind == SCOPEITEM_VAR) {
|
||||||
|
return xv_sz(v, sz);
|
||||||
|
} else if(v->kind == SCOPEITEM_SYMBOL) {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), v->data.symbol.name);
|
||||||
|
} else abort();
|
||||||
|
} else if(e->nodeKind == AST_EXPR_PRIMITIVE) {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "%s %i", spec(type_size(e->exprPrim.type)), e->exprPrim.val);
|
||||||
|
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == SCOPEITEM_SYMBOL) {
|
||||||
|
pr = snprintf(ret, XOPBUFSZ, "%s %s", spec(type_size(e->exprUnOp.type)), e->exprUnOp.operand->exprVar.thing->data.symbol.name);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(pr < XOPBUFSZ && "XOPBUF OVERFLOW");
|
||||||
|
|
||||||
|
bufidx = (bufidx + 1) % XOPBUFS;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *xop(AST *tlc, AST *e) {
|
||||||
|
return xop_sz(tlc, e, type_size(e->expression.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *xop_strip_size(const char *str) {
|
||||||
|
if(!strncmp(str, "byte ", 5)) {
|
||||||
|
return str + 5;
|
||||||
|
} else if(!strncmp(str, "word ", 5)) {
|
||||||
|
return str + 5;
|
||||||
|
} else if(!strncmp(str, "dword ", 6)) {
|
||||||
|
return str + 6;
|
||||||
|
} else if(!strncmp(str, "qword ", 6)) {
|
||||||
|
return str + 6;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lr_empty(ScopeItem *a) {
|
||||||
|
assert(a->kind == SCOPEITEM_VAR);
|
||||||
|
//assert(!a->data.var.liveRangeStart == !a->data.var.liveRangeEnd);
|
||||||
|
|
||||||
|
return !a->data.var.liveRangeStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_resource_in_use_right_now_at_this_current_point_in_time_and_also_not_created_right_now(AST *tlc, AST *statement, size_t resource) {
|
||||||
|
for(size_t vi = 0; vi < tlc->chunk.varCount; vi++) {
|
||||||
|
ScopeItem *si = tlc->chunk.vars[vi];
|
||||||
|
if((REG_CLASSES[si->data.var.registerClass].rs[si->data.var.color] & resource) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(ast_stmt_is_after(tlc, statement, si->data.var.liveRangeEnd) == 1 || statement == si->data.var.liveRangeEnd) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(si->data.var.usedefFirst == NULL) {
|
||||||
|
// Even if the resource is in use, the variable itself isn't so we don't care
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!lr_empty(si) && (ast_stmt_is_after(tlc, statement, si->data.var.liveRangeStart) == 1
|
||||||
|
&& ast_stmt_is_after(tlc, si->data.var.liveRangeEnd, statement) == 1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cg_chunk(CGState *cg, AST *a) {
|
||||||
|
AST *s = a->chunk.statementFirst;
|
||||||
|
|
||||||
|
if(a->chunk.stackReservation) {
|
||||||
|
if(x86_ia16()) {
|
||||||
|
printf("push bp\n");
|
||||||
|
printf("mov bp, sp\n");
|
||||||
|
printf("sub sp, %lu\n", a->chunk.stackReservation);
|
||||||
|
} else {
|
||||||
|
printf("sub esp, %lu\n", a->chunk.stackReservation);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < MAX_REGS_PER_CLASS && cg->calleeSaved.reg[i]; i++) {
|
||||||
|
if(x86_ia16()) {
|
||||||
|
printf("mov [bp + %li], %s\n", cg->calleeSaved.stackOffset[i] - a->chunk.stackReservation, cg->calleeSaved.reg[i]);
|
||||||
|
} else {
|
||||||
|
printf("mov [esp + %li], %s\n", cg->calleeSaved.stackOffset[i], cg->calleeSaved.reg[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If there's no stack reservation, there can't be callee-saved regs.
|
||||||
|
assert(cg->calleeSaved.reg[0] == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Potentially complex pattern matching
|
||||||
|
while(s) {
|
||||||
|
if(s->nodeKind == AST_STMT_LABEL) {
|
||||||
|
|
||||||
|
printf(".%s:\n", s->stmtLabel.name);
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_JUMP) {
|
||||||
|
|
||||||
|
if(s->stmtJump.condition) {
|
||||||
|
assert(s->stmtJump.condition->nodeKind == AST_EXPR_BINARY_OP && binop_is_comparison(s->stmtJump.condition->exprBinOp.operator));
|
||||||
|
printf("cmp %s, %s\n", xop(cg->tlc, s->stmtJump.condition->exprBinOp.operands[0]), xop(cg->tlc, s->stmtJump.condition->exprBinOp.operands[1]));
|
||||||
|
printf("j%s .%s\n", xj(s->stmtJump.condition->exprBinOp.operator), s->stmtJump.label);
|
||||||
|
} else {
|
||||||
|
printf("jmp .%s\n", s->stmtJump.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_EXT_ALIGN) {
|
||||||
|
|
||||||
|
uint32_t val = s->stmtExtAlign.val;
|
||||||
|
if((val & (val - 1))) {
|
||||||
|
// nasm does not support non-PoT alignments, so pad manually
|
||||||
|
printf("times ($ - $$ + %u) / %u * %u - ($ - $$) db 0\n", val - 1, val, val);
|
||||||
|
} else {
|
||||||
|
printf("align %u\n", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_DECL) {
|
||||||
|
|
||||||
|
ScopeItem *v = s->stmtDecl.thing;
|
||||||
|
|
||||||
|
if(v->kind == SCOPEITEM_SYMBOL) {
|
||||||
|
|
||||||
|
if(v->data.symbol.isExternal) {
|
||||||
|
// Do nothing.
|
||||||
|
// All external symbols are handled at once in the top-level chunk.
|
||||||
|
//printf("extern %s\n", v->data.symbol.name);
|
||||||
|
} else {
|
||||||
|
if(!v->data.symbol.isLocal) {
|
||||||
|
printf("global %s\n", v->data.symbol.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s->stmtDecl.expression) {
|
||||||
|
printf("%s:", v->data.symbol.name);
|
||||||
|
if(v->type->type == TYPE_TYPE_PRIMITIVE) {
|
||||||
|
|
||||||
|
assert(s->stmtDecl.expression->nodeKind == AST_EXPR_PRIMITIVE);
|
||||||
|
|
||||||
|
printf("%s %i", direct(type_size(v->type)), s->stmtDecl.expression->exprPrim.val);
|
||||||
|
|
||||||
|
} else if(v->type->type == TYPE_TYPE_ARRAY && v->type->array.of->type == TYPE_TYPE_PRIMITIVE) {
|
||||||
|
|
||||||
|
printf("%s ", direct(type_size(v->type->array.of)));
|
||||||
|
for(size_t i = 0; i < v->type->array.length; i++) {
|
||||||
|
printf("%i,", s->stmtDecl.expression->exprArray.items[i]->exprPrim.val);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(v->type->type == TYPE_TYPE_FUNCTION) {
|
||||||
|
|
||||||
|
putchar('\n');
|
||||||
|
|
||||||
|
assert(s->stmtDecl.expression->nodeKind == AST_EXPR_FUNC);
|
||||||
|
|
||||||
|
// Generic functions have non-NULL code blocks
|
||||||
|
if(!type_is_generic(s->stmtDecl.expression->expression.type)) {
|
||||||
|
|
||||||
|
ast_segmented_dereference(s->stmtDecl.expression->exprFunc.chunk);
|
||||||
|
|
||||||
|
ast_sroa(s->stmtDecl.expression->exprFunc.chunk);
|
||||||
|
|
||||||
|
ast_linearize(s->stmtDecl.expression->exprFunc.chunk);
|
||||||
|
|
||||||
|
arch_normalize(s->stmtDecl.expression->exprFunc.chunk);
|
||||||
|
while(!cg_attempt_sections(s->stmtDecl.expression->exprFunc.chunk)) {
|
||||||
|
arch_normalize(s->stmtDecl.expression->exprFunc.chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
cg_go_sections(s->stmtDecl.expression->exprFunc.chunk);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else abort();
|
||||||
|
putchar('\n');
|
||||||
|
} else {
|
||||||
|
|
||||||
|
printf("%s resb %lu\n", v->data.symbol.name, type_size(s->stmtDecl.thing->type));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(v->kind == SCOPEITEM_VAR) {
|
||||||
|
assert(s->stmtDecl.expression == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_ASSIGN && s->stmtAssign.to && s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_CALL) {
|
||||||
|
|
||||||
|
AST *e = s->stmtAssign.to;
|
||||||
|
|
||||||
|
if(is_resource_in_use_right_now_at_this_current_point_in_time_and_also_not_created_right_now(a, s, HWR_ECX)) {
|
||||||
|
puts(x86_ia16() ? "push cx" : "push ecx");
|
||||||
|
}
|
||||||
|
if(is_resource_in_use_right_now_at_this_current_point_in_time_and_also_not_created_right_now(a, s, HWR_EDX)) {
|
||||||
|
puts(x86_ia16() ? "push dx" : "push edx");
|
||||||
|
}
|
||||||
|
if(is_resource_in_use_right_now_at_this_current_point_in_time_and_also_not_created_right_now(a, s, HWR_EAX)) {
|
||||||
|
puts(x86_ia16() ? "push ax" : "push eax");
|
||||||
|
}
|
||||||
|
|
||||||
|
int argCount = e->exprCall.what->expression.type->pointer.of->function.argCount;
|
||||||
|
|
||||||
|
size_t argSize = 0;
|
||||||
|
|
||||||
|
for(int i = argCount - 1; i >= 0; i--) {
|
||||||
|
printf("push %s\n", xop_sz(cg->tlc, e->exprCall.args[i], arch_gpr_size()));
|
||||||
|
|
||||||
|
argSize += (type_size(e->exprCall.args[i]->expression.type) + 3) & ~3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e->exprCall.what->nodeKind == AST_EXPR_VAR && e->exprCall.what->exprVar.thing->kind == SCOPEITEM_SYMBOL) {
|
||||||
|
printf("call %s\n", e->exprCall.what->exprVar.thing->data.symbol.name);
|
||||||
|
} else {
|
||||||
|
printf("call %s\n", xop(cg->tlc, e->exprCall.what));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(argSize) {
|
||||||
|
if(x86_ia16()) {
|
||||||
|
printf("add sp, %lu\n", argSize);
|
||||||
|
} else {
|
||||||
|
printf("add esp, %lu\n", argSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(is_resource_in_use_right_now_at_this_current_point_in_time_and_also_not_created_right_now(a, s, HWR_EAX)) {
|
||||||
|
puts(x86_ia16() ? "pop ax" : "pop eax");
|
||||||
|
}
|
||||||
|
if(is_resource_in_use_right_now_at_this_current_point_in_time_and_also_not_created_right_now(a, s, HWR_EDX)) {
|
||||||
|
puts(x86_ia16() ? "pop dx" : "pop edx");
|
||||||
|
}
|
||||||
|
if(is_resource_in_use_right_now_at_this_current_point_in_time_and_also_not_created_right_now(a, s, HWR_ECX)) {
|
||||||
|
puts(x86_ia16() ? "pop cx" : "pop ecx");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_ASSIGN) {
|
||||||
|
|
||||||
|
if(s->stmtAssign.to && is_xop(s->stmtAssign.what) == XOP_NOT_MEM && is_xop(s->stmtAssign.to) == XOP_NOT_MEM && is_str_equal_check_null(xop(cg->tlc, s->stmtAssign.what), xop(cg->tlc, s->stmtAssign.to))) {
|
||||||
|
// It's a noop
|
||||||
|
} else if(s->stmtAssign.to) {
|
||||||
|
if(x86_imul_supported() && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_MUL) {
|
||||||
|
assert(s->stmtAssign.what->nodeKind == AST_EXPR_VAR);
|
||||||
|
assert(s->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR);
|
||||||
|
|
||||||
|
if(!strcmp(xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[0]))) {
|
||||||
|
printf("imul %s, %s\n", xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[1]));
|
||||||
|
} else {
|
||||||
|
printf("imul %s, %s, %s\n", xop(a, s->stmtAssign.what), xop(a, s->stmtAssign.to->exprBinOp.operands[0]), xop(a, s->stmtAssign.to->exprBinOp.operands[1]));
|
||||||
|
}
|
||||||
|
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_MULHI) {
|
||||||
|
assert(s->stmtAssign.what->nodeKind == AST_EXPR_VAR);
|
||||||
|
assert(s->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR);
|
||||||
|
//assert(s->stmtAssign.what->exprVar.thing->data.var.color == COLOR_EDX);
|
||||||
|
|
||||||
|
assert(s->statement.next->nodeKind == AST_STMT_ASSIGN);
|
||||||
|
assert(s->statement.next->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP);
|
||||||
|
assert(s->statement.next->stmtAssign.to->exprBinOp.operator == BINOP_MUL);
|
||||||
|
|
||||||
|
AST *otherop = NULL;
|
||||||
|
if(ast_expression_equal(s->statement.next->stmtAssign.to->exprBinOp.operands[0], s->statement.next->stmtAssign.what)) {
|
||||||
|
otherop = s->statement.next->stmtAssign.to->exprBinOp.operands[1];
|
||||||
|
} else if(ast_expression_equal(s->statement.next->stmtAssign.to->exprBinOp.operands[1], s->statement.next->stmtAssign.what)) {
|
||||||
|
otherop = s->statement.next->stmtAssign.to->exprBinOp.operands[0];
|
||||||
|
} else abort();
|
||||||
|
|
||||||
|
printf("mul %s\n", xop(a, otherop));
|
||||||
|
|
||||||
|
// Skip next statement, because they come in a pair
|
||||||
|
s = s->statement.next;
|
||||||
|
|
||||||
|
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0]) && (s->stmtAssign.to->exprBinOp.operator == BINOP_ADD || s->stmtAssign.to->exprBinOp.operator == BINOP_SUB) && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && s->stmtAssign.to->exprBinOp.operands[1]->exprPrim.val == 1) {
|
||||||
|
|
||||||
|
// inc or dec
|
||||||
|
|
||||||
|
static const char *instrs[] = {"inc", "dec"};
|
||||||
|
printf("%s %s\n", instrs[s->stmtAssign.to->exprBinOp.operator == BINOP_SUB], xop(cg->tlc, s->stmtAssign.what));
|
||||||
|
|
||||||
|
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0]) && s->stmtAssign.to->exprBinOp.operator < BINOP_SIMPLES) {
|
||||||
|
|
||||||
|
printf("%s %s, %s\n", BINOP_SIMPLE_INSTRS[s->stmtAssign.to->exprBinOp.operator], xop(cg->tlc, s->stmtAssign.what), xop(cg->tlc, s->stmtAssign.to->exprBinOp.operands[1]));
|
||||||
|
|
||||||
|
} else if(x86_is_lea(s)) {
|
||||||
|
|
||||||
|
printf("lea %s, [%s + %s]\n",
|
||||||
|
xop(cg->tlc, s->stmtAssign.what),
|
||||||
|
xop_strip_size(xop(cg->tlc, s->stmtAssign.to->exprBinOp.operands[0])),
|
||||||
|
xop_strip_size(xop(cg->tlc, s->stmtAssign.to->exprBinOp.operands[1])));
|
||||||
|
|
||||||
|
} else if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && s->stmtAssign.to->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == SCOPEITEM_SYMBOL && s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||||
|
|
||||||
|
printf("lea %s, [%s + %s]\n",
|
||||||
|
xv(s->stmtAssign.what->exprVar.thing),
|
||||||
|
s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
|
||||||
|
xv(s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing));
|
||||||
|
|
||||||
|
} else if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && s->stmtAssign.to->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing->kind == SCOPEITEM_VAR) {
|
||||||
|
|
||||||
|
printf("lea %s, [%s + %i]\n",
|
||||||
|
xv_sz(s->stmtAssign.what->exprVar.thing, 0),
|
||||||
|
xv_sz(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing, 0),
|
||||||
|
s->stmtAssign.to->exprBinOp.operands[1]->exprPrim.val);
|
||||||
|
|
||||||
|
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_NEGATE && ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprUnOp.operand)) {
|
||||||
|
|
||||||
|
printf("neg %s\n", xop(cg->tlc, s->stmtAssign.what));
|
||||||
|
|
||||||
|
} else if(is_xop(s->stmtAssign.what) && s->stmtAssign.to->nodeKind == AST_EXPR_CAST) {
|
||||||
|
|
||||||
|
if(type_size(s->stmtAssign.what->expression.type) > type_size(s->stmtAssign.to->exprCast.what->expression.type)) {
|
||||||
|
|
||||||
|
printf("movzx %s, %s\n", xop(cg->tlc, s->stmtAssign.what), xop(cg->tlc, s->stmtAssign.to->exprCast.what));
|
||||||
|
|
||||||
|
} else if(type_size(s->stmtAssign.what->expression.type) > type_size(s->stmtAssign.to->exprCast.what->expression.type)) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LEMMA: For every register in x86, its lowest bits are accessible.
|
||||||
|
*/
|
||||||
|
|
||||||
|
assert(0 && "Not implemented.");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
const char *dest = xop_sz(cg->tlc, s->stmtAssign.what, x86_max_gpr_size());
|
||||||
|
const char *src = xop_sz(cg->tlc, s->stmtAssign.to->exprCast.what, x86_max_gpr_size());
|
||||||
|
|
||||||
|
if(strcmp(dest, src)) {
|
||||||
|
printf("mov %s, %s\n", dest, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
printf("mov %s, %s\n", xop(cg->tlc, s->stmtAssign.what), xop(cg->tlc, s->stmtAssign.to));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} /*else if(s->nodeKind == AST_STMT_LOOP) {
|
||||||
|
|
||||||
|
size_t lbl0 = nextLocalLabel++;
|
||||||
|
size_t lbl1 = nextLocalLabel++;
|
||||||
|
|
||||||
|
cg->loopStackStart[cg->loopStackIdx] = lbl0;
|
||||||
|
cg->loopStackEnd[cg->loopStackIdx] = lbl1;
|
||||||
|
cg->loopStackIdx++;
|
||||||
|
|
||||||
|
printf(".L%lu:\n", lbl0);
|
||||||
|
|
||||||
|
cg_chunk(cg, s->stmtLoop.body);
|
||||||
|
|
||||||
|
printf("jmp .L%lu\n", lbl0);
|
||||||
|
|
||||||
|
printf(".L%lu:\n", lbl1);
|
||||||
|
|
||||||
|
cg->loopStackIdx--;
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_BREAK) {
|
||||||
|
|
||||||
|
printf("jmp .L%lu\n", cg->loopStackEnd[cg->loopStackIdx - 1]);
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_CONTINUE) {
|
||||||
|
|
||||||
|
printf("jmp .L%lu\n", cg->loopStackStart[cg->loopStackIdx - 1]);
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_IF) {
|
||||||
|
|
||||||
|
assert(s->stmtIf.expression->nodeKind == AST_EXPR_BINARY_OP && binop_is_comparison(s->stmtIf.expression->exprBinOp.operator));
|
||||||
|
|
||||||
|
size_t lbl = nextLocalLabel++;
|
||||||
|
|
||||||
|
printf("cmp %s, %s\n", xop(cg->tlc, s->stmtIf.expression->exprBinOp.operands[0]), xop(cg->tlc, s->stmtIf.expression->exprBinOp.operands[1]));
|
||||||
|
printf("j%s .L%lu\n", xj(binop_comp_opposite(s->stmtIf.expression->exprBinOp.operator)), lbl);
|
||||||
|
|
||||||
|
cg_chunk(cg, s->stmtIf.then);
|
||||||
|
|
||||||
|
printf(".L%lu:\n", lbl);
|
||||||
|
|
||||||
|
}*/ else if(s->nodeKind == AST_STMT_RETURN) {
|
||||||
|
|
||||||
|
if(s->stmtReturn.val) {
|
||||||
|
assert(s->stmtReturn.val->nodeKind == AST_EXPR_VAR);
|
||||||
|
assert(s->stmtReturn.val->exprVar.thing->kind == SCOPEITEM_VAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(a->chunk.stackReservation) {
|
||||||
|
for(int i = 0; i < MAX_REGS_PER_CLASS && cg->calleeSaved.reg[i]; i++) {
|
||||||
|
if(x86_ia16()) {
|
||||||
|
printf("mov %s, [bp + %li]\n", cg->calleeSaved.reg[i], cg->calleeSaved.stackOffset[i] - a->chunk.stackReservation);
|
||||||
|
} else {
|
||||||
|
printf("mov %s, [esp + %li]\n", cg->calleeSaved.reg[i], cg->calleeSaved.stackOffset[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(x86_ia16()) {
|
||||||
|
printf("add sp, %lu\n", a->chunk.stackReservation);
|
||||||
|
} else {
|
||||||
|
printf("add esp, %lu\n", a->chunk.stackReservation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(x86_ia16()) {
|
||||||
|
if(x86_target() >= I186) {
|
||||||
|
printf("leave\n");
|
||||||
|
} else {
|
||||||
|
printf("mov sp, bp\n");
|
||||||
|
printf("pop bp\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("ret\n");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
stahp_node(s, "Unknown statement %s caught by code generator.", AST_KIND_STR[s->nodeKind]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
s = s->statement.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef ScopeItem *Adjacency[2];
|
||||||
|
|
||||||
|
static bool var_collision(AST *tlc, ScopeItem *v1, ScopeItem *v2) {
|
||||||
|
/* 1D intersection test */
|
||||||
|
bool liveRangeIntersection = !lr_empty(v1) && !lr_empty(v2) && (v1->data.var.liveRangeStart == v2->data.var.liveRangeStart || (
|
||||||
|
(ast_stmt_is_after(tlc, v1->data.var.liveRangeStart, v2->data.var.liveRangeStart) == 1
|
||||||
|
&& ast_stmt_is_after(tlc, v2->data.var.liveRangeEnd, v1->data.var.liveRangeStart) == 1)
|
||||||
|
||
|
||||||
|
(ast_stmt_is_after(tlc, v1->data.var.liveRangeEnd, v2->data.var.liveRangeStart) == 1
|
||||||
|
&& ast_stmt_is_after(tlc, v2->data.var.liveRangeEnd, v1->data.var.liveRangeEnd) == 1)
|
||||||
|
));
|
||||||
|
|
||||||
|
bool resourceIntersection = (REG_CLASSES[v1->data.var.registerClass].rMask & REG_CLASSES[v2->data.var.registerClass].rMask) != 0;
|
||||||
|
|
||||||
|
return liveRangeIntersection && resourceIntersection;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void callee_saved(AST *tlc, struct CalleeSavedState *state) {
|
||||||
|
bool ebxused = false, ediused = false, esiused = false, ebpused = false;
|
||||||
|
|
||||||
|
for(size_t v = 0; v < tlc->chunk.varCount; v++) {
|
||||||
|
size_t resource = REG_CLASSES[tlc->chunk.vars[v]->data.var.registerClass].rs[tlc->chunk.vars[v]->data.var.color];
|
||||||
|
|
||||||
|
if(resource & HWR_EBX) {
|
||||||
|
ebxused = true;
|
||||||
|
}
|
||||||
|
if(resource & HWR_EDI) {
|
||||||
|
ediused = true;
|
||||||
|
}
|
||||||
|
if(resource & HWR_ESI) {
|
||||||
|
esiused = true;
|
||||||
|
}
|
||||||
|
if(resource & HWR_EBP) {
|
||||||
|
ebpused = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t nextUser = 0;
|
||||||
|
if(ebxused) {
|
||||||
|
state->stackOffset[nextUser] = nextUser * x86_max_gpr_size();
|
||||||
|
state->reg[nextUser] = x86_ia16() ? "bx" : "ebx";
|
||||||
|
nextUser++;
|
||||||
|
}
|
||||||
|
if(esiused) {
|
||||||
|
state->stackOffset[nextUser] = nextUser * x86_max_gpr_size();
|
||||||
|
state->reg[nextUser] = x86_ia16() ? "si" : "esi";
|
||||||
|
nextUser++;
|
||||||
|
}
|
||||||
|
if(ediused) {
|
||||||
|
state->stackOffset[nextUser] = nextUser * x86_max_gpr_size();
|
||||||
|
state->reg[nextUser] = x86_ia16() ? "di" : "edi";
|
||||||
|
nextUser++;
|
||||||
|
}
|
||||||
|
if(ebpused) {
|
||||||
|
state->stackOffset[nextUser] = nextUser * x86_max_gpr_size();
|
||||||
|
state->reg[nextUser] = x86_ia16() ? "bp" : "ebp";
|
||||||
|
nextUser++;
|
||||||
|
}
|
||||||
|
ast_grow_stack_frame(tlc, nextUser * x86_max_gpr_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void determine_register_classes(AST *tlc) {
|
||||||
|
for(size_t v = 0; v < tlc->chunk.varCount; v++) {
|
||||||
|
if(tlc->chunk.vars[v]->data.var.preclassed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type_size(tlc->chunk.vars[v]->type) == 1) {
|
||||||
|
tlc->chunk.vars[v]->data.var.registerClass = REG_CLASS_8;
|
||||||
|
} else {
|
||||||
|
tlc->chunk.vars[v]->data.var.registerClass = x86_ia16() ? REG_CLASS_IA16_USEABLE : REG_CLASS_NOT_8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Welsh-Powell graph coloring */
|
||||||
|
static int comparator(const void *A, const void *B) {
|
||||||
|
ScopeItem *const *a = A;
|
||||||
|
ScopeItem *const *b = B;
|
||||||
|
return ((*a)->data.var.degree * (*a)->data.var.priority) - ((*b)->data.var.degree * (*b)->data.var.priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int assign_coloring(AST *a, ScopeItem **vars, size_t adjCount, Adjacency *adjs, bool preclassed) {
|
||||||
|
int mustSpillRegisterClass = -1;
|
||||||
|
|
||||||
|
/* Welsh plow my ass */
|
||||||
|
for(int v = 0; v < a->chunk.varCount; v++) {
|
||||||
|
if(vars[v]->data.var.preclassed != preclassed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(vars[v]->data.var.color != -1) {
|
||||||
|
// Already assigned.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int c = 0;; c++) {
|
||||||
|
if(c >= MAX_REGS_PER_CLASS || REG_CLASSES[vars[v]->data.var.registerClass].rs[c] == 0) {
|
||||||
|
// Ran out of resources
|
||||||
|
mustSpillRegisterClass = vars[v]->data.var.registerClass;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int resource = REG_CLASSES[vars[v]->data.var.registerClass].rs[c];
|
||||||
|
|
||||||
|
if(REG_CLASSES[vars[v]->data.var.registerClass].rsS[c] != type_size(vars[v]->type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int a = 0; a < adjCount; a++) {
|
||||||
|
ScopeItem *me = NULL, *they = NULL;
|
||||||
|
|
||||||
|
if(adjs[a][0] == vars[v]) {
|
||||||
|
me = adjs[a][0];
|
||||||
|
they = adjs[a][1];
|
||||||
|
} else if(adjs[a][1] == vars[v]) {
|
||||||
|
me = adjs[a][1];
|
||||||
|
they = adjs[a][0];
|
||||||
|
} else continue;
|
||||||
|
|
||||||
|
if(they->data.var.color == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int theyRes = REG_CLASSES[they->data.var.registerClass].rs[they->data.var.color];
|
||||||
|
|
||||||
|
if((resource & theyRes) != 0) {
|
||||||
|
goto nextColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vars[v]->data.var.color = c;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
nextColor:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mustSpillRegisterClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cg_tlc(AST *a) {
|
||||||
|
assert(a->nodeKind == AST_CHUNK);
|
||||||
|
|
||||||
|
for(size_t e = 0; e < a->chunk.externCount; e++) {
|
||||||
|
assert(a->chunk.externs[e]->kind == SCOPEITEM_SYMBOL);
|
||||||
|
assert(a->chunk.externs[e]->data.symbol.isExternal);
|
||||||
|
|
||||||
|
printf("extern %s\n", a->chunk.externs[e]->data.symbol.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CalleeSavedState calleeSaved = {};
|
||||||
|
if(a->chunk.functionType) {
|
||||||
|
callee_saved(a, &calleeSaved);
|
||||||
|
}
|
||||||
|
|
||||||
|
CGState cg;
|
||||||
|
memset(&cg, 0, sizeof(cg));
|
||||||
|
cg.tlc = a;
|
||||||
|
cg.isFunction = !!a->chunk.functionType;
|
||||||
|
cg.calleeSaved = calleeSaved;
|
||||||
|
|
||||||
|
cg_chunk(&cg, a);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cg_attempt(AST *a) {
|
||||||
|
ast_usedef_reset(a);
|
||||||
|
|
||||||
|
size_t adjCount = 0;
|
||||||
|
Adjacency *adjs = calloc(adjCount, sizeof(*adjs));
|
||||||
|
|
||||||
|
ScopeItem **vars = a->chunk.vars;
|
||||||
|
|
||||||
|
for(size_t vi = 0; vi < a->chunk.varCount; vi++) {
|
||||||
|
vars[vi]->data.var.priority = 1;
|
||||||
|
vars[vi]->data.var.degree = 0;
|
||||||
|
|
||||||
|
if(!vars[vi]->data.var.preclassed) {
|
||||||
|
vars[vi]->data.var.registerClass = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!vars[vi]->data.var.precolored) {
|
||||||
|
vars[vi]->data.var.color = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
determine_register_classes(a);
|
||||||
|
|
||||||
|
for(size_t v1i = 0; v1i < a->chunk.varCount; v1i++) {
|
||||||
|
for(size_t v2i = 0; v2i < a->chunk.varCount; v2i++) {
|
||||||
|
if(v1i == v2i) continue;
|
||||||
|
|
||||||
|
ScopeItem *v1 = vars[v1i];
|
||||||
|
ScopeItem *v2 = vars[v2i];
|
||||||
|
|
||||||
|
if(var_collision(a, v1, v2)) {
|
||||||
|
ScopeItem *min = v1 < v2 ? v1 : v2;
|
||||||
|
ScopeItem *max = v1 < v2 ? v2 : v1;
|
||||||
|
|
||||||
|
for(size_t a = 0; a < adjCount; a++) {
|
||||||
|
if(adjs[a][0] == min && adjs[a][1] == max) {
|
||||||
|
goto cont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
adjs = realloc(adjs, sizeof(*adjs) * ++adjCount);
|
||||||
|
adjs[adjCount - 1][0] = min;
|
||||||
|
adjs[adjCount - 1][1] = max;
|
||||||
|
|
||||||
|
cont:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t a = 0; a < adjCount; a++) {
|
||||||
|
adjs[a][0]->data.var.degree++;
|
||||||
|
adjs[a][1]->data.var.degree++;
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort(vars, a->chunk.varCount, sizeof(*vars), comparator);
|
||||||
|
|
||||||
|
int mustSpillRegisterClass;
|
||||||
|
|
||||||
|
// Important to prioritize preclassed variables
|
||||||
|
mustSpillRegisterClass = assign_coloring(a, vars, adjCount, adjs, true);
|
||||||
|
if(mustSpillRegisterClass == -1) {
|
||||||
|
mustSpillRegisterClass = assign_coloring(a, vars, adjCount, adjs, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(adjs);
|
||||||
|
|
||||||
|
if(mustSpillRegisterClass != -1) {
|
||||||
|
// Spill node with highest degree
|
||||||
|
|
||||||
|
ScopeItem *chosen = NULL;
|
||||||
|
for(ssize_t i = a->chunk.varCount - 1; i >= 0; i--) {
|
||||||
|
if(vars[i]->data.var.registerClass == mustSpillRegisterClass && !vars[i]->data.var.precolored) {
|
||||||
|
chosen = vars[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(chosen != NULL);
|
||||||
|
|
||||||
|
if(ntc_get_int("pdbg")) {
|
||||||
|
char *astdump = ast_dump(a);
|
||||||
|
fprintf(stderr, "### CG FAILURE ###\n%s\n", astdump);
|
||||||
|
free(astdump);
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_spill_to_stack(a, chosen);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cg_attempt_sections(AST *a) {
|
||||||
|
assert(a->nodeKind == AST_SECTION);
|
||||||
|
|
||||||
|
ASTSection *current = &a->section;
|
||||||
|
while(current) {
|
||||||
|
if(!cg_attempt(current->tlc)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cg_go_sections(AST *a) {
|
||||||
|
assert(a->nodeKind == AST_SECTION);
|
||||||
|
|
||||||
|
ASTSection *current = &a->section;
|
||||||
|
while(current) {
|
||||||
|
if(current->name.content) {
|
||||||
|
printf("section %s\n", current->name.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
cg_tlc(current->tlc);
|
||||||
|
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
826
src/x86/normalize.c
Normal file
826
src/x86/normalize.c
Normal file
@@ -0,0 +1,826 @@
|
|||||||
|
#include<stdlib.h>
|
||||||
|
#include<assert.h>
|
||||||
|
#include<string.h>
|
||||||
|
|
||||||
|
#include"arch.h"
|
||||||
|
#include"reporting.h"
|
||||||
|
#include"utils.h"
|
||||||
|
|
||||||
|
// This is the normalization pass:
|
||||||
|
// Complex expressions are to be "broken down" into simpler ones until
|
||||||
|
// the AST can be trivially translated to the target architecture.
|
||||||
|
// This file along with CG is strictly for IA-32 & IA-16 and will fail
|
||||||
|
// for other architectures.
|
||||||
|
|
||||||
|
static ScopeItem *create_temp(AST *tlc, Type *itstype) {
|
||||||
|
static size_t vidx = 0;
|
||||||
|
return ast_tlc_new_var(tlc, malp("nrm%lu", vidx++), itstype);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Split away complex expression into a new local variable */
|
||||||
|
static AST *varify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
|
||||||
|
ScopeItem *vte = create_temp(tlc, e->expression.type);
|
||||||
|
|
||||||
|
// Alter AST
|
||||||
|
|
||||||
|
ASTExprVar *ev[2];
|
||||||
|
for(int i = 0; i < 2; i++) {
|
||||||
|
ev[i] = calloc(1, sizeof(ASTExprVar));
|
||||||
|
ev[i]->nodeKind = AST_EXPR_VAR;
|
||||||
|
ev[i]->type = e->expression.type;
|
||||||
|
ev[i]->thing = vte;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTStmtAssign *assign = calloc(1, sizeof(*assign));
|
||||||
|
assign->nodeKind = AST_STMT_ASSIGN;
|
||||||
|
assign->what = (AST*) ev[0];
|
||||||
|
assign->to = e;
|
||||||
|
vte->data.var.declaration = (AST*) assign;
|
||||||
|
|
||||||
|
if(stmtPrev) {
|
||||||
|
stmtPrev->statement.next = (AST*) assign;
|
||||||
|
} else {
|
||||||
|
chunk->chunk.statementFirst = (AST*) assign;
|
||||||
|
}
|
||||||
|
assign->next = stmt;
|
||||||
|
|
||||||
|
// Reset ud-chain
|
||||||
|
|
||||||
|
vte->data.var.usedefFirst = NULL;
|
||||||
|
vte->data.var.usedefLast = NULL;
|
||||||
|
|
||||||
|
return (AST*) ev[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
static AST *xopify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
|
||||||
|
return varify(tlc, chunk, stmtPrev, stmt, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is needed for architectures where not all registers can be used
|
||||||
|
// as pointers (for example IA-16 or the 68000 series)
|
||||||
|
static void mark_ptr(AST *a) {
|
||||||
|
assert(a->nodeKind == AST_EXPR_VAR);
|
||||||
|
assert(a->exprVar.thing->kind == SCOPEITEM_VAR);
|
||||||
|
|
||||||
|
if(x86_ia16()) {
|
||||||
|
assert(!a->exprVar.thing->data.var.preclassed || a->exprVar.thing->data.var.registerClass == REG_CLASS_IA16_PTRS);
|
||||||
|
|
||||||
|
a->exprVar.thing->data.var.preclassed = true;
|
||||||
|
a->exprVar.thing->data.var.registerClass = REG_CLASS_IA16_PTRS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mark_a(ScopeItem *si) {
|
||||||
|
size_t sz = type_size(si->type);
|
||||||
|
if(sz <= 1) {
|
||||||
|
vte_precolor(si, REG_CLASS_8, 0);
|
||||||
|
} else if(sz == 2) {
|
||||||
|
vte_precolor(si, REG_CLASS_16_32, 0);
|
||||||
|
} else if(sz == 4 || sz == 0) {
|
||||||
|
vte_precolor(si, REG_CLASS_16_32, 1);
|
||||||
|
} else {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mark_d(ScopeItem *si) {
|
||||||
|
size_t sz = type_size(si->type);
|
||||||
|
if(sz <= 1) {
|
||||||
|
vte_precolor(si, REG_CLASS_8, 6);
|
||||||
|
} else if(sz == 2) {
|
||||||
|
vte_precolor(si, REG_CLASS_16_32, 6);
|
||||||
|
} else if(sz == 4 || sz == 0) {
|
||||||
|
vte_precolor(si, REG_CLASS_16_32, 7);
|
||||||
|
} else {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RULE ONE OF DUMBING: NEVER xopify NOR varify MORE THAN ONCE IN A SINGLE CALL TO VISITOR!!!
|
||||||
|
* IF YOU DO THIS, stmtPrev WILL FUCK UP AND STATEMENTS WILL BE LOST
|
||||||
|
*/
|
||||||
|
struct NormState {
|
||||||
|
AST *targetTLC;
|
||||||
|
int effective;
|
||||||
|
};
|
||||||
|
static void normalize_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chu, AST *tlc, void *ud) {
|
||||||
|
struct NormState *this = ud;
|
||||||
|
|
||||||
|
AST *s = *nptr;
|
||||||
|
|
||||||
|
if(s->nodeKind == AST_STMT_JUMP && s->stmtJump.condition) {
|
||||||
|
|
||||||
|
AST *e = s->stmtJump.condition;
|
||||||
|
|
||||||
|
if(e->nodeKind == AST_EXPR_BINARY_OP && binop_is_comparison(e->exprBinOp.operator)) {
|
||||||
|
|
||||||
|
if(is_xop(e->exprBinOp.operands[0]) == XOP_NOT_XOP) {
|
||||||
|
e->exprBinOp.operands[0] = xopify(tlc, chu, stmtPrev, s, e->exprBinOp.operands[0]);
|
||||||
|
this->effective = 1;
|
||||||
|
} else if(is_xop(e->exprBinOp.operands[1]) == XOP_NOT_XOP) {
|
||||||
|
e->exprBinOp.operands[1] = xopify(tlc, chu, stmtPrev, s, e->exprBinOp.operands[1]);
|
||||||
|
this->effective = 1;
|
||||||
|
} else if(is_xop(e->exprBinOp.operands[0]) == XOP_MEM && is_xop(e->exprBinOp.operands[1]) == XOP_MEM) {
|
||||||
|
// Can't have two mems; put one in var
|
||||||
|
|
||||||
|
e->exprBinOp.operands[1] = varify(tlc, chu, stmtPrev, s, e->exprBinOp.operands[1]);
|
||||||
|
this->effective = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_NOT && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && (e->exprUnOp.operand->exprBinOp.operator == BINOP_LOGICAL_AND || e->exprUnOp.operand->exprBinOp.operator == BINOP_LOGICAL_OR)) {
|
||||||
|
|
||||||
|
AST *binop = e->exprUnOp.operand;
|
||||||
|
|
||||||
|
e->exprUnOp.operand = binop->exprBinOp.operands[0];
|
||||||
|
binop->exprBinOp.operands[0] = e;
|
||||||
|
|
||||||
|
AST *unop2 = calloc(1, sizeof(ASTExprUnaryOp));
|
||||||
|
unop2->nodeKind = AST_EXPR_UNARY_OP;
|
||||||
|
unop2->expression.type = binop->exprBinOp.operands[1]->expression.type;
|
||||||
|
unop2->exprUnOp.operator = UNOP_NOT;
|
||||||
|
unop2->exprUnOp.operand = binop->exprBinOp.operands[1];
|
||||||
|
binop->exprBinOp.operands[1] = unop2;
|
||||||
|
|
||||||
|
binop->exprBinOp.operator = binop->exprBinOp.operator == BINOP_LOGICAL_AND ? BINOP_LOGICAL_OR : BINOP_LOGICAL_AND;
|
||||||
|
|
||||||
|
s->stmtJump.condition = binop;
|
||||||
|
|
||||||
|
ast_denoop(tlc, &s->stmtJump.condition);
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
|
||||||
|
} else if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_LOGICAL_OR) {
|
||||||
|
|
||||||
|
AST *cond0 = e->exprBinOp.operands[0];
|
||||||
|
AST *cond1 = e->exprBinOp.operands[1];
|
||||||
|
|
||||||
|
s->stmtJump.condition = cond0;
|
||||||
|
|
||||||
|
AST *jump2 = calloc(1, sizeof(ASTStmtJump));
|
||||||
|
jump2->nodeKind = AST_STMT_JUMP;
|
||||||
|
jump2->stmtJump.condition = cond1;
|
||||||
|
jump2->stmtJump.label = strdup(s->stmtJump.label);
|
||||||
|
jump2->statement.next = s->statement.next;
|
||||||
|
s->statement.next = jump2;
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
|
||||||
|
} else if(e->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operator == BINOP_LOGICAL_AND) {
|
||||||
|
|
||||||
|
static size_t idx = 0;
|
||||||
|
|
||||||
|
AST *cond0 = e->exprBinOp.operands[0];
|
||||||
|
AST *cond1 = e->exprBinOp.operands[1];
|
||||||
|
|
||||||
|
AST *lbl = calloc(1, sizeof(ASTStmtLabel));
|
||||||
|
lbl->nodeKind = AST_STMT_LABEL;
|
||||||
|
lbl->stmtLabel.name = malp("$dla%lu", idx++);
|
||||||
|
lbl->statement.next = s->statement.next;
|
||||||
|
|
||||||
|
AST *unop = calloc(1, sizeof(ASTExprUnaryOp));
|
||||||
|
unop->nodeKind = AST_EXPR_UNARY_OP;
|
||||||
|
unop->expression.type = cond0->expression.type;
|
||||||
|
unop->exprUnOp.operator = UNOP_NOT;
|
||||||
|
unop->exprUnOp.operand = cond0;
|
||||||
|
s->stmtJump.condition = unop;
|
||||||
|
|
||||||
|
AST *jump2 = calloc(1, sizeof(ASTStmtJump));
|
||||||
|
jump2->nodeKind = AST_STMT_JUMP;
|
||||||
|
jump2->stmtJump.condition = cond1;
|
||||||
|
jump2->stmtJump.label = strdup(s->stmtJump.label);
|
||||||
|
jump2->statement.next = lbl;
|
||||||
|
|
||||||
|
s->stmtJump.label = strdup(lbl->stmtLabel.name);
|
||||||
|
|
||||||
|
s->statement.next = jump2;
|
||||||
|
|
||||||
|
ast_denoop(tlc, &s->stmtJump.condition);
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
AST *v = e;
|
||||||
|
|
||||||
|
bool not = v->nodeKind == AST_EXPR_UNARY_OP && v->exprUnOp.operator == UNOP_NOT;
|
||||||
|
if(not) {
|
||||||
|
v = v->exprUnOp.operand;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(v->nodeKind != AST_EXPR_VAR) {
|
||||||
|
v = varify(tlc, chu, stmtPrev, s, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
AST *zero = calloc(1, sizeof(ASTExprPrimitive));
|
||||||
|
zero->nodeKind = AST_EXPR_PRIMITIVE;
|
||||||
|
zero->expression.type = v->expression.type;
|
||||||
|
zero->exprPrim.val = 0;
|
||||||
|
|
||||||
|
AST *binop = calloc(1, sizeof(ASTExprBinaryOp));
|
||||||
|
binop->nodeKind = AST_EXPR_BINARY_OP;
|
||||||
|
binop->expression.type = v->expression.type;
|
||||||
|
binop->exprBinOp.operator = not ? BINOP_EQUAL : BINOP_NEQUAL;
|
||||||
|
binop->exprBinOp.operands[0] = v;
|
||||||
|
binop->exprBinOp.operands[1] = zero;
|
||||||
|
|
||||||
|
s->stmtJump.condition = binop;
|
||||||
|
|
||||||
|
ast_denoop(tlc, &s->stmtJump.condition);
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_LOOP) {
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_RETURN) {
|
||||||
|
|
||||||
|
// ret returns in eax always, so it needs to be in a precolored var
|
||||||
|
AST *retval = s->stmtReturn.val;
|
||||||
|
|
||||||
|
if(retval && (!is_xop(retval) || retval->nodeKind == AST_EXPR_PRIMITIVE || (retval->nodeKind == AST_EXPR_VAR && retval->exprVar.thing->kind == SCOPEITEM_VAR && (!retval->exprVar.thing->data.var.precolored || !strchr(REG_CLASSES[retval->exprVar.thing->data.var.registerClass].rsN[retval->exprVar.thing->data.var.color], 'a'))))) {
|
||||||
|
|
||||||
|
retval = s->stmtReturn.val = varify(tlc, chu, stmtPrev, s, retval);
|
||||||
|
this->effective = 1;
|
||||||
|
|
||||||
|
mark_a(retval->exprVar.thing);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_CALL) {
|
||||||
|
|
||||||
|
// Turn this:
|
||||||
|
// f(x);
|
||||||
|
// into:
|
||||||
|
// a = f(x);
|
||||||
|
|
||||||
|
s->stmtExpr.expr = varify(tlc, chu, stmtPrev, s, s->stmtExpr.expr);
|
||||||
|
|
||||||
|
mark_a(s->stmtExpr.expr->exprVar.thing);
|
||||||
|
|
||||||
|
// Purge this statement entirely, otherwise we'd have
|
||||||
|
// a = f(x);
|
||||||
|
// a;
|
||||||
|
|
||||||
|
if(stmtPrev) {
|
||||||
|
assert(stmtPrev->statement.next->statement.next == s);
|
||||||
|
stmtPrev->statement.next->statement.next = s->statement.next;
|
||||||
|
} else {
|
||||||
|
assert(chu->chunk.statementFirst->statement.next == s);
|
||||||
|
chu->chunk.statementFirst->statement.next = s->statement.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
|
||||||
|
} else if(s->nodeKind == AST_STMT_ASSIGN && s->stmtAssign.to) {
|
||||||
|
|
||||||
|
if(ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to) && stmtPrev) {
|
||||||
|
// This is a NOP, remove it from the AST
|
||||||
|
stmtPrev->statement.next = s->statement.next;
|
||||||
|
this->effective = 1;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if(s->stmtAssign.to->nodeKind == AST_EXPR_CALL && (s->stmtAssign.what->nodeKind != AST_EXPR_VAR || s->stmtAssign.what->exprVar.thing->kind != SCOPEITEM_VAR || !s->stmtAssign.what->exprVar.thing->data.var.precolored)) {
|
||||||
|
|
||||||
|
ScopeItem *tmp = create_temp(tlc, s->stmtAssign.what->expression.type);
|
||||||
|
mark_a(tmp);
|
||||||
|
|
||||||
|
ASTExprVar *ev[2] = {calloc(1, sizeof(**ev)), calloc(1, sizeof(**ev))};
|
||||||
|
ev[0]->nodeKind = AST_EXPR_VAR;
|
||||||
|
ev[0]->type = tmp->type;
|
||||||
|
ev[0]->thing = tmp;
|
||||||
|
memcpy(ev[1], ev[0], sizeof(**ev));
|
||||||
|
|
||||||
|
ASTStmtAssign *assign2 = calloc(1, sizeof(*assign2));
|
||||||
|
assign2->nodeKind = AST_STMT_ASSIGN;
|
||||||
|
assign2->what = (AST*) s->stmtAssign.what;
|
||||||
|
assign2->to = (AST*) ev[0];
|
||||||
|
tmp->data.var.declaration = (AST*) s;
|
||||||
|
|
||||||
|
s->stmtAssign.what = (AST*) ev[1];
|
||||||
|
|
||||||
|
assign2->next = s->stmtAssign.next;
|
||||||
|
s->stmtAssign.next = (AST*) assign2;
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
|
||||||
|
} else if(s->stmtAssign.what->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.what->exprBinOp.operator == BINOP_DEREF
|
||||||
|
&& s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_DEREF) {
|
||||||
|
|
||||||
|
s->stmtAssign.to = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to);
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
|
||||||
|
} else if(s->stmtAssign.what->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.what->exprBinOp.operator == BINOP_DEREF && !is_xop(s->stmtAssign.what)) {
|
||||||
|
|
||||||
|
s->stmtAssign.what->exprBinOp.operands[0] = varify(tlc, chu, stmtPrev, s, s->stmtAssign.what->exprBinOp.operands[0]);
|
||||||
|
|
||||||
|
mark_ptr(s->stmtAssign.what->exprBinOp.operands[0]);
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
|
||||||
|
} else if(s->stmtAssign.what && s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_CALL && is_xop(s->stmtAssign.to->exprCall.what) == XOP_NOT_XOP) {
|
||||||
|
|
||||||
|
s->stmtAssign.to->exprCall.what = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprCall.what);
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
|
||||||
|
} else if(s->stmtAssign.what && s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == SCOPEITEM_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_CALL) {
|
||||||
|
ASTExprCall *call = &s->stmtAssign.to->exprCall;
|
||||||
|
|
||||||
|
int argCount = call->what->expression.type->pointer.of->function.argCount;
|
||||||
|
|
||||||
|
for(int i = 0; i < argCount; i++) {
|
||||||
|
// We also check for ast_references_stack because the stack gets
|
||||||
|
// shifted during a function call which screws up all of the offsets
|
||||||
|
if(is_xop(call->args[i]) == XOP_NOT_XOP || ast_references_stack(call->args[i])) {
|
||||||
|
call->args[i] = xopify(tlc, chu, stmtPrev, s, call->args[i]);
|
||||||
|
this->effective = 1;
|
||||||
|
|
||||||
|
// Xopify one argument at a time otherwise stmtPrev becomes incorrect
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(s->stmtAssign.to && s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_NEGATE && !ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprUnOp.operand)) {
|
||||||
|
// Turn this:
|
||||||
|
// a = -b
|
||||||
|
// into
|
||||||
|
// a = b
|
||||||
|
// a = -a
|
||||||
|
|
||||||
|
AST *ev[2] = {
|
||||||
|
ast_deep_copy(s->stmtAssign.what),
|
||||||
|
ast_deep_copy(s->stmtAssign.what)
|
||||||
|
};
|
||||||
|
|
||||||
|
AST *negation = s->stmtAssign.to;
|
||||||
|
|
||||||
|
s->stmtAssign.to = negation->exprUnOp.operand;
|
||||||
|
negation->exprUnOp.operand = ev[0];
|
||||||
|
|
||||||
|
AST *assign2 = calloc(1, sizeof(ASTStmtAssign));
|
||||||
|
assign2->nodeKind = AST_STMT_ASSIGN;
|
||||||
|
assign2->stmtAssign.what = ev[1];
|
||||||
|
assign2->stmtAssign.to = negation;
|
||||||
|
|
||||||
|
assign2->statement.next = s->statement.next;
|
||||||
|
s->statement.next = assign2;
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
} else if(is_xop(s->stmtAssign.to) == XOP_NOT_XOP) {
|
||||||
|
if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprUnOp.operator == BINOP_DEREF) {
|
||||||
|
s->stmtAssign.to->exprBinOp.operands[0] = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[0]);
|
||||||
|
|
||||||
|
mark_ptr(s->stmtAssign.to->exprBinOp.operands[0]);
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_MUL) {
|
||||||
|
|
||||||
|
WhysItBad_Huh because = x86_test_mul(stmtPrev, s);
|
||||||
|
|
||||||
|
if(because == SRC0_IS_BAD_XOP) {
|
||||||
|
s->stmtAssign.to->exprBinOp.operands[0] = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[0]);
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
} else if(because == SRC1_IS_BAD_XOP) {
|
||||||
|
s->stmtAssign.to->exprBinOp.operands[1] = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[1]);
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
} else if(because == MISSING_MULHI_FOR_MUL || because == DEST_NOT_SRC0) {// !stmtPrev || stmtPrev->nodeKind != AST_STMT_ASSIGN || stmtPrev->stmtAssign.to->nodeKind != AST_EXPR_BINARY_OP || stmtPrev->stmtAssign.to->exprBinOp.operator != BINOP_MULHI) {
|
||||||
|
// Turn this:
|
||||||
|
// a = b * c
|
||||||
|
// Into this:
|
||||||
|
// d = b
|
||||||
|
// e = d *^ c
|
||||||
|
// d = d * c
|
||||||
|
// a = d
|
||||||
|
|
||||||
|
ASTExprVar *originalDestination = s->stmtAssign.what;
|
||||||
|
|
||||||
|
s->stmtAssign.to->exprBinOp.operands[0] = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[0]);
|
||||||
|
|
||||||
|
assert(stmtPrev->statement.next->nodeKind == AST_STMT_ASSIGN && stmtPrev->statement.next->stmtAssign.what->nodeKind == AST_EXPR_VAR && ast_expression_equal(stmtPrev->statement.next->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0]));
|
||||||
|
|
||||||
|
if(!x86_imul_supported()) {
|
||||||
|
ASTExprBinaryOp *mulhi = calloc(1, sizeof(*mulhi));
|
||||||
|
mulhi->nodeKind = AST_EXPR_BINARY_OP;
|
||||||
|
mulhi->type = s->stmtAssign.to->expression.type;
|
||||||
|
mulhi->operator = BINOP_MULHI;
|
||||||
|
mulhi->operands[0] = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[0]);
|
||||||
|
mulhi->operands[1] = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[1]);
|
||||||
|
|
||||||
|
AST *hihalf = varify(tlc, chu, stmtPrev->statement.next, s, mulhi);
|
||||||
|
|
||||||
|
mark_d(hihalf->exprVar.thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
s->stmtAssign.what = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[0]);
|
||||||
|
|
||||||
|
ASTStmtAssign *redest = calloc(1, sizeof(*redest));
|
||||||
|
redest->nodeKind = AST_STMT_ASSIGN;
|
||||||
|
redest->what = originalDestination;
|
||||||
|
redest->to = ast_deep_copy(s->stmtAssign.to->exprBinOp.operands[0]);
|
||||||
|
redest->next = s->stmtAssign.next;
|
||||||
|
|
||||||
|
s->stmtAssign.next = (AST*) redest;
|
||||||
|
|
||||||
|
mark_a(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing);
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
} else assert(because == NOT_AT_ALL_IT || because == GUCCI);
|
||||||
|
|
||||||
|
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator < BINOP_SIMPLES && !ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprBinOp.operands[0])) {
|
||||||
|
// Turn this:
|
||||||
|
// a = b op c
|
||||||
|
// into
|
||||||
|
// a = b
|
||||||
|
// a = a op c
|
||||||
|
|
||||||
|
if(x86_is_lea(s)) {
|
||||||
|
// We do the lea trick in cg.c
|
||||||
|
} else {
|
||||||
|
AST *assign2 = calloc(1, sizeof(ASTStmtAssign));
|
||||||
|
assign2->nodeKind = AST_STMT_ASSIGN;
|
||||||
|
assign2->stmtAssign.what = ast_deep_copy(s->stmtAssign.what);
|
||||||
|
assign2->stmtAssign.to = s->stmtAssign.to->exprBinOp.operands[0];
|
||||||
|
|
||||||
|
if(stmtPrev) {
|
||||||
|
stmtPrev->statement.next = assign2;
|
||||||
|
} else {
|
||||||
|
chu->chunk.statementFirst = assign2;
|
||||||
|
}
|
||||||
|
assign2->statement.next = s;
|
||||||
|
|
||||||
|
s->stmtAssign.to->exprBinOp.operands[0] = ast_deep_copy(s->stmtAssign.what);
|
||||||
|
|
||||||
|
this->effective = 1;
|
||||||
|
}
|
||||||
|
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && !is_xop(s->stmtAssign.to->exprBinOp.operands[0])) {
|
||||||
|
s->stmtAssign.to->exprBinOp.operands[0] = xopify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[0]);
|
||||||
|
this->effective = 1;
|
||||||
|
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && !is_xop(s->stmtAssign.to->exprBinOp.operands[1])) {
|
||||||
|
s->stmtAssign.to->exprBinOp.operands[1] = xopify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprBinOp.operands[1]);
|
||||||
|
this->effective = 1;
|
||||||
|
} else if(s->stmtAssign.to->nodeKind == AST_EXPR_CAST && s->stmtAssign.to->exprCast.what->nodeKind != AST_EXPR_VAR) {
|
||||||
|
s->stmtAssign.to->exprCast.what = varify(tlc, chu, stmtPrev, s, s->stmtAssign.to->exprCast.what);
|
||||||
|
this->effective = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pre_norm_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
AST *n = *nptr;
|
||||||
|
|
||||||
|
if(n->nodeKind == AST_CHUNK) {
|
||||||
|
if(n->chunk.functionType) {
|
||||||
|
size_t argCount = n->chunk.functionType->function.argCount;
|
||||||
|
|
||||||
|
// First argCount vtes in list are the arguments
|
||||||
|
for(size_t i = 0; i < argCount; i++) {
|
||||||
|
ScopeItem *vte = n->chunk.vars[i];
|
||||||
|
|
||||||
|
ASTExprStackPointer *stck = calloc(1, sizeof(*stck));
|
||||||
|
stck->nodeKind = AST_EXPR_STACK_POINTER;
|
||||||
|
stck->type = type_pointer_wrap(vte->type);
|
||||||
|
|
||||||
|
ASTExprPrimitive *offset = calloc(1, sizeof(*offset));
|
||||||
|
offset->nodeKind = AST_EXPR_PRIMITIVE;
|
||||||
|
offset->type = type_u(8 * x86_max_gpr_size());
|
||||||
|
offset->val = 4 + i * x86_max_gpr_size();
|
||||||
|
offset->stackGrowth = true;
|
||||||
|
|
||||||
|
ASTExprBinaryOp *sum = calloc(1, sizeof(*sum));
|
||||||
|
sum->nodeKind = AST_EXPR_BINARY_OP;
|
||||||
|
sum->type = stck->type;
|
||||||
|
sum->operands[0] = (AST*) stck;
|
||||||
|
sum->operands[1] = (AST*) offset;
|
||||||
|
sum->operator = BINOP_ADD;
|
||||||
|
|
||||||
|
ASTExprBinaryOp *deref = calloc(1, sizeof(*deref));
|
||||||
|
deref->nodeKind = AST_EXPR_BINARY_OP;
|
||||||
|
deref->type = vte->type;
|
||||||
|
deref->operands[0] = (AST*) sum;
|
||||||
|
deref->operator = BINOP_DEREF;
|
||||||
|
|
||||||
|
ASTExprVar *evar = calloc(1, sizeof(*evar));
|
||||||
|
evar->nodeKind = AST_EXPR_VAR;
|
||||||
|
evar->type = vte->type;
|
||||||
|
evar->thing = vte;
|
||||||
|
|
||||||
|
ASTStmtAssign *ass = calloc(1, sizeof(*ass));
|
||||||
|
ass->nodeKind = AST_STMT_ASSIGN;
|
||||||
|
ass->next = NULL;
|
||||||
|
ass->what = (AST*) evar;
|
||||||
|
ass->to = ast_cast_expr((AST*) deref, vte->type); // Must cast because of "convention correctness"
|
||||||
|
ass->next = n->chunk.statementFirst;
|
||||||
|
vte->data.var.declaration = (AST*) ass;
|
||||||
|
|
||||||
|
if(n->chunk.statementFirst) {
|
||||||
|
n->chunk.statementFirst = ass;
|
||||||
|
} else {
|
||||||
|
assert(!n->chunk.statementLast);
|
||||||
|
n->chunk.statementFirst = n->chunk.statementLast = ass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void decompose_symbol_record_field_access(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
AST *n = *nptr;
|
||||||
|
|
||||||
|
if(n->nodeKind == AST_EXPR_DOT) {
|
||||||
|
ASTExprUnaryOp *ref = calloc(1, sizeof(*ref));
|
||||||
|
ref->nodeKind = AST_EXPR_UNARY_OP;
|
||||||
|
ref->type = type_pointer_wrap(n->exprDot.a->expression.type);
|
||||||
|
ref->operator = UNOP_REF;
|
||||||
|
ref->operand = n->exprDot.a;
|
||||||
|
|
||||||
|
ASTExprPrimitive *offset = calloc(1, sizeof(*offset));
|
||||||
|
offset->nodeKind = AST_EXPR_PRIMITIVE;
|
||||||
|
offset->type = primitive_parse("u32");
|
||||||
|
|
||||||
|
size_t f;
|
||||||
|
for(f = 0; f < n->exprDot.a->expression.type->record.fieldCount; f++) {
|
||||||
|
if(!strcmp(n->exprDot.a->expression.type->record.fieldNames[f], n->exprDot.b)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(f < n->exprDot.a->expression.type->record.fieldCount);
|
||||||
|
|
||||||
|
offset->val = n->exprDot.a->expression.type->record.fieldOffsets[f];
|
||||||
|
|
||||||
|
ASTExprBinaryOp *sum = calloc(1, sizeof(*sum));
|
||||||
|
sum->nodeKind = AST_EXPR_BINARY_OP;
|
||||||
|
sum->type = ref->type;
|
||||||
|
sum->operator = BINOP_ADD;
|
||||||
|
sum->operands[0] = (AST*) ref;
|
||||||
|
sum->operands[1] = (AST*) offset;
|
||||||
|
|
||||||
|
AST *cast = ast_cast_expr((AST*) sum, type_pointer_wrap(n->exprDot.a->expression.type->record.fieldTypes[f]));
|
||||||
|
|
||||||
|
ASTExprBinaryOp *deref = calloc(1, sizeof(*deref));
|
||||||
|
deref->nodeKind = AST_EXPR_BINARY_OP;
|
||||||
|
deref->type = cast->expression.type->pointer.of;
|
||||||
|
deref->operator = BINOP_DEREF;
|
||||||
|
deref->operands[0] = cast;
|
||||||
|
|
||||||
|
*nptr = (AST*) deref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_pointer2pointer_cast(AST *e) {
|
||||||
|
return e->nodeKind == AST_EXPR_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_double_field_access(AST *e) {
|
||||||
|
return e->nodeKind == AST_EXPR_BINARY_OP && is_pointer2pointer_cast(e->exprBinOp.operands[0]) && e->exprBinOp.operands[0]->exprCast.what->nodeKind == AST_EXPR_BINARY_OP && e->exprBinOp.operands[0]->exprCast.what->exprBinOp.operator == e->exprBinOp.operator && e->exprBinOp.operator == BINOP_ADD && e->exprBinOp.operands[0]->exprCast.what->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && e->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DenoopState {
|
||||||
|
AST *targetTLC;
|
||||||
|
bool success;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void denoop_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
struct DenoopState *state = ud;
|
||||||
|
|
||||||
|
AST *n = *nptr;
|
||||||
|
|
||||||
|
bool *success = &state->success;
|
||||||
|
|
||||||
|
if(n->nodeKind == AST_EXPR_NULL) {
|
||||||
|
// Turn `null` into integer
|
||||||
|
|
||||||
|
ASTExprPrimitive *prim = calloc(1, sizeof(*prim));
|
||||||
|
prim->nodeKind = AST_EXPR_PRIMITIVE;
|
||||||
|
prim->type = n->expression.type;
|
||||||
|
prim->val = ntc_get_int_default("null", 0);
|
||||||
|
|
||||||
|
*nptr = (AST*) prim;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_REF && n->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && n->exprUnOp.operand->exprBinOp.operator == BINOP_DEREF) {
|
||||||
|
// Turn `&*a` into `a`
|
||||||
|
|
||||||
|
// Artificially change type of casted expression to keep types valid for subsequent passes
|
||||||
|
n->exprUnOp.operand->exprBinOp.operands[0]->expression.type = n->expression.type;
|
||||||
|
|
||||||
|
*nptr = n->exprUnOp.operand->exprBinOp.operands[0];
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
} else if(is_double_field_access(n)) {
|
||||||
|
// Turn `(p + x) as C + y` into `(p + (x + y)) as C`
|
||||||
|
|
||||||
|
n->exprBinOp.operands[0]->exprCast.what->exprBinOp.operands[1]->exprPrim.val += n->exprBinOp.operands[1]->exprPrim.val;
|
||||||
|
|
||||||
|
*nptr = n->exprBinOp.operands[0];
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_CAST && n->exprCast.what->nodeKind == AST_EXPR_CAST) {
|
||||||
|
// Turn `(p as A) as B` to `p as B`
|
||||||
|
|
||||||
|
n->exprCast.what = n->exprCast.what->exprCast.what;
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operands[0]->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operator == BINOP_ADD && n->exprBinOp.operands[0]->exprBinOp.operator == BINOP_ADD && n->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[0]->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && !(n->exprBinOp.operands[0]->exprBinOp.operands[1]->exprPrim.stackGrowth && n->exprBinOp.operands[1]->exprPrim.stackGrowth)) {
|
||||||
|
// Turn `(x + a) + b` into `x + (a + b)`
|
||||||
|
|
||||||
|
n->exprBinOp.operands[0]->exprBinOp.operands[1]->exprPrim.val += n->exprBinOp.operands[1]->exprPrim.val;
|
||||||
|
n->exprBinOp.operands[0]->exprBinOp.operands[1]->exprPrim.stackGrowth = n->exprBinOp.operands[0]->exprBinOp.operands[1]->exprPrim.stackGrowth || n->exprBinOp.operands[1]->exprPrim.stackGrowth;
|
||||||
|
|
||||||
|
*nptr = n->exprBinOp.operands[0];
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operator == BINOP_ADD && n->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[1]->exprPrim.val == 0 && !n->exprBinOp.operands[1]->exprPrim.stackGrowth) {
|
||||||
|
// Turn `x + 0` into `x`
|
||||||
|
|
||||||
|
// Artificially change type of casted expression to keep types valid for subsequent passes
|
||||||
|
n->exprBinOp.operands[0]->expression.type = n->expression.type;
|
||||||
|
|
||||||
|
*nptr = n->exprBinOp.operands[0];
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_NOT && n->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && binop_comp_opposite(n->exprUnOp.operand->exprBinOp.operator) != BINOP_WTF) {
|
||||||
|
// Turn `!(a op b)` to `(a !op b)`
|
||||||
|
|
||||||
|
n->exprUnOp.operand->exprBinOp.operator = binop_comp_opposite(n->exprUnOp.operand->exprBinOp.operator);
|
||||||
|
|
||||||
|
*nptr = n->exprUnOp.operand;
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operator == UNOP_NOT && n->exprUnOp.operand->nodeKind == AST_EXPR_UNARY_OP && n->exprUnOp.operand->exprUnOp.operator == UNOP_NOT) {
|
||||||
|
// Turn `!!x` to `x`
|
||||||
|
|
||||||
|
*nptr = n->exprUnOp.operand->exprUnOp.operand;
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_CAST && n->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && n->exprCast.to->type == TYPE_TYPE_POINTER) {
|
||||||
|
// Turn (x as A*) into x, since all pointer types are identical in Nectar's AST
|
||||||
|
|
||||||
|
// Artificially change type of casted expression to keep types valid for subsequent passes
|
||||||
|
n->exprCast.what->expression.type = n->exprCast.to;
|
||||||
|
|
||||||
|
*nptr = n->exprCast.what;
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operator == BINOP_ADD && n->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && !(n->exprBinOp.operands[0]->exprPrim.stackGrowth && n->exprBinOp.operands[1]->exprPrim.stackGrowth)) {
|
||||||
|
// Constant propagation of + operator
|
||||||
|
|
||||||
|
AST *prim = n->exprBinOp.operands[0];
|
||||||
|
prim->expression.type = n->exprBinOp.type;
|
||||||
|
prim->exprPrim.val = n->exprBinOp.operands[0]->exprPrim.val + n->exprBinOp.operands[1]->exprPrim.val;
|
||||||
|
prim->exprPrim.stackGrowth = n->exprBinOp.operands[0]->exprPrim.stackGrowth || n->exprBinOp.operands[1]->exprPrim.stackGrowth;
|
||||||
|
|
||||||
|
*nptr = prim;
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operator == BINOP_SUB && n->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && n->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && !(n->exprBinOp.operands[0]->exprPrim.stackGrowth && n->exprBinOp.operands[1]->exprPrim.stackGrowth)) {
|
||||||
|
// Constant propagation of - operator
|
||||||
|
|
||||||
|
AST *prim = n->exprBinOp.operands[0];
|
||||||
|
prim->expression.type = n->exprBinOp.type;
|
||||||
|
prim->exprPrim.val = n->exprBinOp.operands[0]->exprPrim.val - n->exprBinOp.operands[1]->exprPrim.val;
|
||||||
|
prim->exprPrim.stackGrowth = n->exprBinOp.operands[0]->exprPrim.stackGrowth || n->exprBinOp.operands[1]->exprPrim.stackGrowth;
|
||||||
|
|
||||||
|
*nptr = prim;
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_EXT_SIZEOF) {
|
||||||
|
ASTExprPrimitive *prim = calloc(1, sizeof(*prim));
|
||||||
|
prim->nodeKind = AST_EXPR_PRIMITIVE;
|
||||||
|
prim->type = n->expression.type;
|
||||||
|
|
||||||
|
assert(!!n->exprExtSizeOf.ofExpr != !!n->exprExtSizeOf.ofType);
|
||||||
|
|
||||||
|
if(n->exprExtSizeOf.ofType) {
|
||||||
|
prim->val = type_size(n->exprExtSizeOf.ofType);
|
||||||
|
} else {
|
||||||
|
prim->val = type_size(n->exprExtSizeOf.ofExpr->expression.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
*nptr = (AST*) prim;
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
} else if(n->nodeKind == AST_EXPR_CAST) {
|
||||||
|
// TODO: Move all compile-time constant casting from ast_cast_expr to here.
|
||||||
|
|
||||||
|
AST *what = n->exprCast.what;
|
||||||
|
Type *to = n->exprCast.to;
|
||||||
|
|
||||||
|
if(what->nodeKind == AST_EXPR_PRIMITIVE && (to->type == TYPE_TYPE_PRIMITIVE || to->type == TYPE_TYPE_POINTER)) {
|
||||||
|
ASTExprPrimitive *ret = calloc(1, sizeof(*ret));
|
||||||
|
ret->nodeKind = AST_EXPR_PRIMITIVE;
|
||||||
|
ret->type = to;
|
||||||
|
|
||||||
|
if(to->type == TYPE_TYPE_PRIMITIVE) {
|
||||||
|
ret->val = what->exprPrim.val & ((1UL << to->primitive.width) - 1);
|
||||||
|
} else {
|
||||||
|
ret->val = what->exprPrim.val & ((1UL << (8 * type_size(to))) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
*nptr = ret;
|
||||||
|
|
||||||
|
*success = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ast_denoop(AST *tlc, AST **node) {
|
||||||
|
if(!node) {
|
||||||
|
node = &tlc;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DenoopState state = {.targetTLC = tlc};
|
||||||
|
do {
|
||||||
|
state.success = false;
|
||||||
|
generic_visitor(node, NULL, NULL, tlc, tlc, &state, denoop_visitor, NULL);
|
||||||
|
} while(state.success);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The convention correctness pass converts all function calls & function sources to the form
|
||||||
|
* hat matches the architecture most closely. For example, arguments (and return values) in
|
||||||
|
* cdecl are always passed as 32-bit integers, even if they are defined as 8-bit or 16-bit in
|
||||||
|
* the source.
|
||||||
|
*
|
||||||
|
* TODO: convert records to proper form also.
|
||||||
|
*/
|
||||||
|
static void convention_correctness_visitor(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
AST *n = *nptr;
|
||||||
|
|
||||||
|
if(n->nodeKind == AST_EXPR_CALL) {
|
||||||
|
Type *type = n->exprCall.what->expression.type;
|
||||||
|
|
||||||
|
assert(type->type == TYPE_TYPE_POINTER);
|
||||||
|
|
||||||
|
type = type->pointer.of;
|
||||||
|
|
||||||
|
assert(type->type == TYPE_TYPE_FUNCTION);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < type->function.argCount; i++) {
|
||||||
|
if(type->function.args[i]->type == TYPE_TYPE_PRIMITIVE) {
|
||||||
|
n->exprCall.args[i] = ast_cast_expr(n->exprCall.args[i], type_prim_cast(type->function.args[i], 8 * x86_max_gpr_size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void decompose_segmented_dereferences(AST **nptr, AST *stmt, AST *stmtPrev, AST *chunk, AST *tlc, void *ud) {
|
||||||
|
AST *n = *nptr;
|
||||||
|
if(n->nodeKind == AST_EXPR_BINARY_OP && n->exprBinOp.operator == BINOP_DEREF && n->exprBinOp.operands[1]) {
|
||||||
|
AST *seg = n->exprBinOp.operands[1];
|
||||||
|
if(seg->nodeKind != AST_EXPR_VAR || seg->exprVar.thing->kind != SCOPEITEM_VAR || seg->exprVar.thing->data.var.registerClass != REG_CLASS_DATASEGS) {
|
||||||
|
seg = n->exprBinOp.operands[1] = varify(tlc, chunk, stmtPrev, stmt, seg);
|
||||||
|
seg->exprVar.thing->data.var.preclassed = true;
|
||||||
|
seg->exprVar.thing->data.var.registerClass = REG_CLASS_DATASEGS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void arch_normalize_pre(AST *tlc) {
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, convention_correctness_visitor, NULL);
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, pre_norm_visitor, NULL);
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, decompose_symbol_record_field_access, NULL);
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, tlc, decompose_segmented_dereferences, NULL);
|
||||||
|
|
||||||
|
for(size_t t = 0; t < tlc->chunk.varCount;) {
|
||||||
|
if(ast_is_scopeitem_referenced(tlc, tlc->chunk.vars[t]) || tlc->chunk.vars[t]->type->type == TYPE_TYPE_RECORD) {
|
||||||
|
ast_spill_to_stack(tlc, tlc->chunk.vars[t]);
|
||||||
|
} else {
|
||||||
|
t++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_denoop(tlc, NULL);
|
||||||
|
|
||||||
|
ast_commutativity_pass(tlc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void arch_normalize(AST* tlc) {
|
||||||
|
size_t i = 0;
|
||||||
|
while(1) {
|
||||||
|
if(i == 20000) {
|
||||||
|
stahp(0, 0, "TOO MANY DUMBS. TOO MANY DUMBS.");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NormState state = {.targetTLC = tlc};
|
||||||
|
|
||||||
|
generic_visitor(&tlc, NULL, NULL, tlc, tlc, &state, normalize_visitor, NULL);
|
||||||
|
|
||||||
|
int successful = state.effective;
|
||||||
|
|
||||||
|
if(!successful) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ntc_get_int("pdbg")) {
|
||||||
|
fprintf(stderr, "### NORM %lu ###\n", i++);
|
||||||
|
char *astdump = ast_dump(tlc);
|
||||||
|
fputs(astdump, stderr);
|
||||||
|
free(astdump);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
u32 x: 123;
|
|
||||||
u33 y: 5;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
extern u32() getchar;
|
|
||||||
extern void(u32) putchar;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
u8 a = getchar();
|
|
||||||
if(a - 48) {
|
|
||||||
putchar(a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
tests/if.nct
11
tests/if.nct
@@ -1,11 +0,0 @@
|
|||||||
u16 x: 5;
|
|
||||||
loop {
|
|
||||||
u16* y = 257;
|
|
||||||
u9 w = -4;
|
|
||||||
u4 z = 3 + *y;
|
|
||||||
u2 o = -w;
|
|
||||||
|
|
||||||
if(x != 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
48
tests/mandelbrot_expected.txt
Normal file
48
tests/mandelbrot_expected.txt
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDEGFFEEEEDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAAAAAAAAABBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDEEEFGIIGFFEEEDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDEEEEFFFI KHGGGHGEDDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAAAAAABBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEEFFGHIMTKLZOGFEEDDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAAAAABBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEEEFGGHHIKPPKIHGFFEEEDDDDDDDDDCCCCCCCCCCBBBBBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAAAABBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEEFFGHIJKS X KHHGFEEEEEDDDDDDDDDCCCCCCCCCCBBBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAAABBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEEFFGQPUVOTY ZQL[MHFEEEEEEEDDDDDDDCCCCCCCCCCCBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAABBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEFFFFFGGHJLZ UKHGFFEEEEEEEEDDDDDCCCCCCCCCCCCBBBBBBBBBBBB
|
||||||
|
AAAAAAABBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEFFFFFFGGGGHIKP KHHGGFFFFEEEEEEDDDDDCCCCCCCCCCCBBBBBBBBBBB
|
||||||
|
AAAAAAABBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDEEEEEFGGHIIHHHHHIIIJKMR VMKJIHHHGFFFFFFGSGEDDDDCCCCCCCCCCCCBBBBBBBBB
|
||||||
|
AAAAAABBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDEEEEEEFFGHK MKJIJO N R X YUSR PLV LHHHGGHIOJGFEDDDCCCCCCCCCCCCBBBBBBBB
|
||||||
|
AAAAABBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDEEEEEEEEEFFFFGH O TN S NKJKR LLQMNHEEDDDCCCCCCCCCCCCBBBBBBB
|
||||||
|
AAAAABBCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDEEEEEEEEEEEEFFFFFGHHIN Q UMWGEEEDDDCCCCCCCCCCCCBBBBBB
|
||||||
|
AAAABBCCCCCCCCCCCCCCCCCCCCCCCCCDDDDEEEEEEEEEEEEEEEFFFFFFGHIJKLOT [JGFFEEEDDCCCCCCCCCCCCCBBBBB
|
||||||
|
AAAABCCCCCCCCCCCCCCCCCCCCCCDDDDEEEEEEEEEEEEEEEEFFFFFFGGHYV RQU QMJHGGFEEEDDDCCCCCCCCCCCCCBBBB
|
||||||
|
AAABCCCCCCCCCCCCCCCCCDDDDDDDEEFJIHFFFFFFFFFFFFFFGGGGGGHIJN JHHGFEEDDDDCCCCCCCCCCCCCBBB
|
||||||
|
AAABCCCCCCCCCCCDDDDDDDDDDEEEEFFHLKHHGGGGHHMJHGGGGGGHHHIKRR UQ L HFEDDDDCCCCCCCCCCCCCCBB
|
||||||
|
AABCCCCCCCCDDDDDDDDDDDEEEEEEFFFHKQMRKNJIJLVS JJKIIIIIIJLR YNHFEDDDDDCCCCCCCCCCCCCBB
|
||||||
|
AABCCCCCDDDDDDDDDDDDEEEEEEEFFGGHIJKOU O O PR LLJJJKL OIHFFEDDDDDCCCCCCCCCCCCCCB
|
||||||
|
AACCCDDDDDDDDDDDDDEEEEEEEEEFGGGHIJMR RMLMN NTFEEDDDDDDCCCCCCCCCCCCCB
|
||||||
|
AACCDDDDDDDDDDDDEEEEEEEEEFGGGHHKONSZ QPR NJGFEEDDDDDDCCCCCCCCCCCCCC
|
||||||
|
ABCDDDDDDDDDDDEEEEEFFFFFGIPJIIJKMQ VX HFFEEDDDDDDCCCCCCCCCCCCCC
|
||||||
|
ACDDDDDDDDDDEFFFFFFFGGGGHIKZOOPPS HGFEEEDDDDDDCCCCCCCCCCCCCC
|
||||||
|
ADEEEEFFFGHIGGGGGGHHHHIJJLNY TJHGFFEEEDDDDDDDCCCCCCCCCCCCC
|
||||||
|
A PLJHGGFFEEEDDDDDDDCCCCCCCCCCCCC
|
||||||
|
ADEEEEFFFGHIGGGGGGHHHHIJJLNY TJHGFFEEEDDDDDDDCCCCCCCCCCCCC
|
||||||
|
ACDDDDDDDDDDEFFFFFFFGGGGHIKZOOPPS HGFEEEDDDDDDCCCCCCCCCCCCCC
|
||||||
|
ABCDDDDDDDDDDDEEEEEFFFFFGIPJIIJKMQ VX HFFEEDDDDDDCCCCCCCCCCCCCC
|
||||||
|
AACCDDDDDDDDDDDDEEEEEEEEEFGGGHHKONSZ QPR NJGFEEDDDDDDCCCCCCCCCCCCCC
|
||||||
|
AACCCDDDDDDDDDDDDDEEEEEEEEEFGGGHIJMR RMLMN NTFEEDDDDDDCCCCCCCCCCCCCB
|
||||||
|
AABCCCCCDDDDDDDDDDDDEEEEEEEFFGGHIJKOU O O PR LLJJJKL OIHFFEDDDDDCCCCCCCCCCCCCCB
|
||||||
|
AABCCCCCCCCDDDDDDDDDDDEEEEEEFFFHKQMRKNJIJLVS JJKIIIIIIJLR YNHFEDDDDDCCCCCCCCCCCCCBB
|
||||||
|
AAABCCCCCCCCCCCDDDDDDDDDDEEEEFFHLKHHGGGGHHMJHGGGGGGHHHIKRR UQ L HFEDDDDCCCCCCCCCCCCCCBB
|
||||||
|
AAABCCCCCCCCCCCCCCCCCDDDDDDDEEFJIHFFFFFFFFFFFFFFGGGGGGHIJN JHHGFEEDDDDCCCCCCCCCCCCCBBB
|
||||||
|
AAAABCCCCCCCCCCCCCCCCCCCCCCDDDDEEEEEEEEEEEEEEEEFFFFFFGGHYV RQU QMJHGGFEEEDDDCCCCCCCCCCCCCBBBB
|
||||||
|
AAAABBCCCCCCCCCCCCCCCCCCCCCCCCCDDDDEEEEEEEEEEEEEEEFFFFFFGHIJKLOT [JGFFEEEDDCCCCCCCCCCCCCBBBBB
|
||||||
|
AAAAABBCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDEEEEEEEEEEEEFFFFFGHHIN Q UMWGEEEDDDCCCCCCCCCCCCBBBBBB
|
||||||
|
AAAAABBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDEEEEEEEEEFFFFGH O TN S NKJKR LLQMNHEEDDDCCCCCCCCCCCCBBBBBBB
|
||||||
|
AAAAAABBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDEEEEEEFFGHK MKJIJO N R X YUSR PLV LHHHGGHIOJGFEDDDCCCCCCCCCCCCBBBBBBBB
|
||||||
|
AAAAAAABBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDEEEEEFGGHIIHHHHHIIIJKMR VMKJIHHHGFFFFFFGSGEDDDDCCCCCCCCCCCCBBBBBBBBB
|
||||||
|
AAAAAAABBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEFFFFFFGGGGHIKP KHHGGFFFFEEEEEEDDDDDCCCCCCCCCCCBBBBBBBBBBB
|
||||||
|
AAAAAAAABBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEFFFFFGGHJLZ UKHGFFEEEEEEEEDDDDDCCCCCCCCCCCCBBBBBBBBBBBB
|
||||||
|
AAAAAAAAABBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEEFFGQPUVOTY ZQL[MHFEEEEEEEDDDDDDDCCCCCCCCCCCBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAAAABBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDEEEEEEFFGHIJKS X KHHGFEEEEEDDDDDDDDDCCCCCCCCCCBBBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAAAAABBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEEEFGGHHIKPPKIHGFFEEEDDDDDDDDDCCCCCCCCCCBBBBBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAAAAAABBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDEEEEEFFGHIMTKLZOGFEEDDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDEEEEFFFI KHGGGHGEDDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
|
AAAAAAAAAAAAAAABBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDEEEFGIIGFFEEEDDDDDDDDCCCCCCCCCBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
12
tests/testbf.sh
Executable file
12
tests/testbf.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
../ntc in=../examples/bf.nct target=386 pdbg=0 > bf.asm
|
||||||
|
nasm -felf -o bf.o bf.asm
|
||||||
|
ld -dynamic-linker /lib/ld-linux.so.2 -melf_i386 -o bf bf.o -lc
|
||||||
|
diff -u mandelbrot_expected.txt <(./bf < ../examples/mandelbrot.b | head -c6239)
|
||||||
|
|
||||||
|
if [ $? == 0 ] ; then
|
||||||
|
echo -e "Mandelbrot test \e[1;32msuccessful\e[0m."
|
||||||
|
else
|
||||||
|
echo -e "Mandelbrot test \e[1;31mfailed\e[0m."
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user