以下をアセンブラに落としていく
PUSH 2
PUSH 3
PUSH 4
MUL
ADD
スタックへのpushとpop
// 2*3を計算して結果をスタックにプッシュ
push 2
push 3
pop rdi
pop rax
mul rax, rdi
push rax
// 4*5を計算して結果をスタックにプッシュ
push 4
push 5
pop rdi
pop rax
mul rax, rdi
push rax
// スタックトップの2つの値を足す
// つまり2*3+4*5を計算する
pop rdi
pop rax
add rax, rdi
push rax
インタプリターでは構文解析の実行のタイミングで実行するがコンパイラはスタックに値を入れて、opに応じてアッセンブラを実行する
RAXはアキュムレータの「A」
RSIはソースインデックスレジスタ
cqo命令を使うと、RAXに入っている64ビットの値を128ビットに伸ばしてRDXとRAXにセットすることができる
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// token type 構造体
typedef enum {
TK_RESERVED, // op
TK_NUM,
TK_EOF, // end of input
} TokenKind;
typedef struct Token Token;
// token
struct Token {
TokenKind kind; // token type
Token *next; // next token
int val; // TK_NUM value
char *str; // token char
};
// now focus token
Token *token;
// input program
char *user_input;
// report error
void error_at(char *loc, char *fmt, ...){
va_list ap;
va_start(ap, fmt);
int pos = loc - user_input;
fprintf(stderr, "%s\n", user_input);
fprintf(stderr, "%*s", pos, " "); // output pos blank
fprintf(stderr, "^ ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
exit(1);
}
void error(char *fmt, ...){
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
exit(1);
}
// if the next token is expected symbol, read forward one token and
// return true.
bool consume(char op) {
if(token->kind != TK_RESERVED | token->str[0] != op)
return false;
token = token->next;
return true;
}
// if the next token is expected symbol, read forward one token and
// otherwise report an error.
void expect(char op) {
if(token->kind != TK_RESERVED | token->str[0] != op)
error("'%c'ではありません。", op);
token = token->next;
}
// if the next token is expected number, read forward one token and
// otherwise report an error.
int expect_number() {
if(token->kind != TK_NUM)
error("数ではありません");
int val = token->val;
token = token->next;
return val;
}
bool at_eof() {
return token->kind == TK_EOF;
}
// create a new token and connect it to curl.
Token *new_token(TokenKind kind, Token *cur, char *str){
Token *tok = calloc(1, sizeof(token));
tok->kind = kind;
tok->str = str;
cur->next = tok;
return tok;
}
// Tokenize the input string p and return it
Token *tokenize(char *p) {
Token head;
head.next = NULL;
Token *cur = &head;
while (*p) {
// skip space
if(isspace(*p)){
p++;
continue;
}
if(*p == '+' || *p == '-') {
cur = new_token(TK_RESERVED, cur, p++);
continue;
}
if(isdigit(*p)) {
cur = new_token(TK_NUM, cur, p);
cur->val = strtol(p, &p, 10);
continue;
}
error("トークナイズできません");
}
new_token(TK_EOF, cur, p);
return head.next;
}
typedef enum {
ND_ADD, // +
ND_SUB, // -
ND_MUL, // *
ND_DIV, // /
ND_NUM, // num
} NodeKind;
typedef struct Node Node;
struct Node {
NodeKind kind; // node type
Node *lhs; // left
Node *rhs; // right
int val;
};
Node *new_node(NodeKind kind, Node *lhs, Node *rhs){
Node *node = calloc(1, sizeof(Node));
node->kind = kind;
node->lhs = lhs;
node->rhs = rhs;
return node;
}
Node *new_node_num(int val) {
Node *node = calloc(1, sizeof(Node));
node->kind = ND_NUM;
node->val = val;
return node;
}
Node *expr();
Node *mul();
Node *primary();
Node *expr() {
Node *node = mul();
for(;;) {
if(consume('+'))
node = new_node(ND_ADD, node, mul());
else if (consume('-'))
node = new_node(ND_SUB, node, mul());
else
return node;
}
}
Node *mul() {
Node *node = primary();
for(;;) {
if(consume('*'))
node = new_node(ND_MUL, node, primary());
else if(consume('/'))
node = new_node(ND_DIV, node, primary());
else
return node;
}
}
Node *primary() {
if(consume('(')){
Node *node = expr();
expect(')');
return node;
}
return new_node_num(expect_number());
}
void gen(Node *node) {
if(node->kind == ND_NUM) {
printf(" push %d\n", node->val);
return;
}
gen(node->lhs);
gen(node->rhs);
printf(" pop rdi\n");
printf(" pop rax\n");
switch(node->kind) {
case ND_ADD:
printf(" addd rax, rdi\n");
break;
case ND_SUB:
printf(" sub rax, rdi\n");
break;
case ND_MUL:
printf(" imul rax, rdi\n");
break;
case ND_DIV:
printf(" cqo\n");
printf(" idiv rdi\n");
break;
}
printf(" push rax\n");
}
int main(int argc, char **argv){
if(argc != 2) {
error("引数の個数が正しくありません\n");
return 1;
}
user_input = argv[1];
token = tokenize(user_input);
Node *node = expr();
printf(".intel_syntax noprefix\n");
printf(".global main\n");
printf("main:\n");
gen(node);
printf(" pop rax\n");
printf(" ret\n");
return 0;
}