Implement usual arithmetic conversion

This commit is contained in:
Rui Ueyama 2020-09-26 10:24:45 +09:00
parent cfc4fa94c1
commit 8b430a6c5f
7 changed files with 93 additions and 7 deletions

View File

@ -146,6 +146,7 @@ struct Node {
int64_t val; // Used if kind == ND_NUM
};
Node *new_cast(Node *expr, Type *ty);
Obj *parse(Token *tok);
//

View File

@ -81,10 +81,15 @@ static void load(Type *ty) {
return;
}
// When we load a char or a short value to a register, we always
// extend them to the size of int, so we can assume the lower half of
// a register always contains a valid value. The upper half of a
// register for char, short and int may contain garbage. When we load
// a long value to a register, it simply occupies the entire register.
if (ty->size == 1)
println(" movsbq (%%rax), %%rax");
println(" movsbl (%%rax), %%eax");
else if (ty->size == 2)
println(" movswq (%%rax), %%rax");
println(" movswl (%%rax), %%eax");
else if (ty->size == 4)
println(" movsxd (%%rax), %%rax");
else

13
parse.c
View File

@ -134,13 +134,20 @@ static Node *new_num(int64_t val, Token *tok) {
return node;
}
static Node *new_long(int64_t val, Token *tok) {
Node *node = new_node(ND_NUM, tok);
node->val = val;
node->ty = ty_long;
return node;
}
static Node *new_var_node(Obj *var, Token *tok) {
Node *node = new_node(ND_VAR, tok);
node->var = var;
return node;
}
static Node *new_cast(Node *expr, Type *ty) {
Node *new_cast(Node *expr, Type *ty) {
add_type(expr);
Node *node = calloc(1, sizeof(Node));
@ -664,7 +671,7 @@ static Node *new_add(Node *lhs, Node *rhs, Token *tok) {
}
// ptr + num
rhs = new_binary(ND_MUL, rhs, new_num(lhs->ty->base->size, tok), tok);
rhs = new_binary(ND_MUL, rhs, new_long(lhs->ty->base->size, tok), tok);
return new_binary(ND_ADD, lhs, rhs, tok);
}
@ -679,7 +686,7 @@ static Node *new_sub(Node *lhs, Node *rhs, Token *tok) {
// ptr - num
if (lhs->ty->base && is_integer(rhs->ty)) {
rhs = new_binary(ND_MUL, rhs, new_num(lhs->ty->base->size, tok), tok);
rhs = new_binary(ND_MUL, rhs, new_long(lhs->ty->base->size, tok), tok);
add_type(rhs);
Node *node = new_binary(ND_SUB, lhs, rhs, tok);
node->ty = lhs->ty;

View File

@ -31,6 +31,8 @@ int main() {
ASSERT(1, 1>=1);
ASSERT(0, 1>=2);
ASSERT(0, 1073741824 * 100 / 100);
printf("OK\n");
return 0;
}

View File

@ -19,6 +19,15 @@ int main() {
ASSERT(48, sizeof(int[3][4]));
ASSERT(8, sizeof(struct {int a; int b;}));
ASSERT(8, sizeof(-10 + (long)5));
ASSERT(8, sizeof(-10 - (long)5));
ASSERT(8, sizeof(-10 * (long)5));
ASSERT(8, sizeof(-10 / (long)5));
ASSERT(8, sizeof((long)-10 + 5));
ASSERT(8, sizeof((long)-10 - 5));
ASSERT(8, sizeof((long)-10 * 5));
ASSERT(8, sizeof((long)-10 / 5));
printf("OK\n");
return 0;
}

28
test/usualconv.c Normal file
View File

@ -0,0 +1,28 @@
#include "test.h"
int main() {
ASSERT((long)-5, -10 + (long)5);
ASSERT((long)-15, -10 - (long)5);
ASSERT((long)-50, -10 * (long)5);
ASSERT((long)-2, -10 / (long)5);
ASSERT(1, -2 < (long)-1);
ASSERT(1, -2 <= (long)-1);
ASSERT(0, -2 > (long)-1);
ASSERT(0, -2 >= (long)-1);
ASSERT(1, (long)-2 < -1);
ASSERT(1, (long)-2 <= -1);
ASSERT(0, (long)-2 > -1);
ASSERT(0, (long)-2 >= -1);
ASSERT(0, 2147483647 + 2147483647 + 2);
ASSERT((long)-1, ({ long x; x=-1; x; }));
ASSERT(1, ({ char x[3]; x[0]=0; x[1]=1; x[2]=2; char *y=x+1; y[0]; }));
ASSERT(0, ({ char x[3]; x[0]=0; x[1]=1; x[2]=2; char *y=x+1; y[-1]; }));
ASSERT(5, ({ struct t {char a;} x, y; x.a=5; y=x; y.a; }));
printf("OK\n");
return 0;
}

38
type.c
View File

@ -47,6 +47,27 @@ Type *array_of(Type *base, int len) {
return ty;
}
static Type *get_common_type(Type *ty1, Type *ty2) {
if (ty1->base)
return pointer_to(ty1->base);
if (ty1->size == 8 || ty2->size == 8)
return ty_long;
return ty_int;
}
// For many binary operators, we implicitly promote operands so that
// both operands have the same type. Any integral type smaller than
// int is always promoted to int. If the type of one operand is larger
// than the other's (e.g. "long" vs. "int"), the smaller operand will
// be promoted to match with the other.
//
// This operation is called the "usual arithmetic conversion".
static void usual_arith_conv(Node **lhs, Node **rhs) {
Type *ty = get_common_type((*lhs)->ty, (*rhs)->ty);
*lhs = new_cast(*lhs, ty);
*rhs = new_cast(*rhs, ty);
}
void add_type(Node *node) {
if (!node || node->ty)
return;
@ -65,23 +86,36 @@ void add_type(Node *node) {
add_type(n);
switch (node->kind) {
case ND_NUM:
node->ty = (node->val == (int)node->val) ? ty_int : ty_long;
return;
case ND_ADD:
case ND_SUB:
case ND_MUL:
case ND_DIV:
case ND_NEG:
usual_arith_conv(&node->lhs, &node->rhs);
node->ty = node->lhs->ty;
return;
case ND_NEG: {
Type *ty = get_common_type(ty_int, node->lhs->ty);
node->lhs = new_cast(node->lhs, ty);
node->ty = ty;
return;
}
case ND_ASSIGN:
if (node->lhs->ty->kind == TY_ARRAY)
error_tok(node->lhs->tok, "not an lvalue");
if (node->lhs->ty->kind != TY_STRUCT)
node->rhs = new_cast(node->rhs, node->lhs->ty);
node->ty = node->lhs->ty;
return;
case ND_EQ:
case ND_NE:
case ND_LT:
case ND_LE:
case ND_NUM:
usual_arith_conv(&node->lhs, &node->rhs);
node->ty = ty_int;
return;
case ND_FUNCALL:
node->ty = ty_long;
return;