From 1433b404d68f9fe314ae2955d0988dd74e5ecb92 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Sun, 6 Sep 2020 21:06:56 +0900 Subject: [PATCH] [GNU] Add __builtin_types_compatible_p --- chibicc.h | 2 ++ parse.c | 10 ++++++++++ test/builtin.c | 33 +++++++++++++++++++++++++++++++++ type.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 test/builtin.c diff --git a/chibicc.h b/chibicc.h index a418d52..fc6ca6d 100644 --- a/chibicc.h +++ b/chibicc.h @@ -284,6 +284,7 @@ struct Type { int size; // sizeof() value int align; // alignment bool is_unsigned; // unsigned or signed + Type *origin; // for type compatibility check // Pointer-to or array-of type. We intentionally use the same member // to represent pointer/array duality in C. @@ -348,6 +349,7 @@ extern Type *ty_double; bool is_integer(Type *ty); bool is_flonum(Type *ty); bool is_numeric(Type *ty); +bool is_compatible(Type *t1, Type *t2); Type *copy_type(Type *ty); Type *pointer_to(Type *base); Type *func_type(Type *return_ty); diff --git a/parse.c b/parse.c index 1fe83d8..ea6ff50 100644 --- a/parse.c +++ b/parse.c @@ -2611,6 +2611,7 @@ static Node *funcall(Token **rest, Token *tok, Node *fn) { // | "sizeof" unary // | "_Alignof" "(" type-name ")" // | "_Alignof" unary +// | "__builtin_types_compatible_p" "(" type-name, type-name, ")" // | "__builtin_reg_class" "(" type-name ")" // | ident // | str @@ -2656,6 +2657,15 @@ static Node *primary(Token **rest, Token *tok) { return new_ulong(node->ty->align, tok); } + if (equal(tok, "__builtin_types_compatible_p")) { + tok = skip(tok->next, "("); + Type *t1 = typename(&tok, tok); + tok = skip(tok, ","); + Type *t2 = typename(&tok, tok); + *rest = skip(tok, ")"); + return new_num(is_compatible(t1, t2), start); + } + if (equal(tok, "__builtin_reg_class")) { tok = skip(tok->next, "("); Type *ty = typename(&tok, tok); diff --git a/test/builtin.c b/test/builtin.c new file mode 100644 index 0000000..178c555 --- /dev/null +++ b/test/builtin.c @@ -0,0 +1,33 @@ +#include "test.h" + +int main() { + ASSERT(1, __builtin_types_compatible_p(int, int)); + ASSERT(1, __builtin_types_compatible_p(double, double)); + ASSERT(0, __builtin_types_compatible_p(int, long)); + ASSERT(0, __builtin_types_compatible_p(long, float)); + ASSERT(1, __builtin_types_compatible_p(int *, int *)); + ASSERT(0, __builtin_types_compatible_p(short *, int *)); + ASSERT(0, __builtin_types_compatible_p(int **, int *)); + ASSERT(1, __builtin_types_compatible_p(const int, int)); + ASSERT(0, __builtin_types_compatible_p(unsigned, int)); + ASSERT(1, __builtin_types_compatible_p(signed, int)); + ASSERT(0, __builtin_types_compatible_p(struct {int a;}, struct {int a;})); + + ASSERT(1, __builtin_types_compatible_p(int (*)(void), int (*)(void))); + ASSERT(1, __builtin_types_compatible_p(void (*)(int), void (*)(int))); + ASSERT(1, __builtin_types_compatible_p(void (*)(int, double), void (*)(int, double))); + ASSERT(1, __builtin_types_compatible_p(int (*)(float, double), int (*)(float, double))); + ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int)); + ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int (*)(float))); + ASSERT(0, __builtin_types_compatible_p(int (*)(float, double), int (*)(float, double, int))); + ASSERT(1, __builtin_types_compatible_p(double (*)(...), double (*)(...))); + ASSERT(0, __builtin_types_compatible_p(double (*)(...), double (*)(void))); + + ASSERT(1, ({ typedef struct {int a;} T; __builtin_types_compatible_p(T, T); })); + ASSERT(1, ({ typedef struct {int a;} T; __builtin_types_compatible_p(T, const T); })); + + ASSERT(1, ({ struct {int a; int b;} x; __builtin_types_compatible_p(typeof(x.a), typeof(x.b)); })); + + printf("OK\n"); + return 0; +} diff --git a/type.c b/type.c index b50c3c3..a774f09 100644 --- a/type.c +++ b/type.c @@ -38,9 +38,56 @@ bool is_numeric(Type *ty) { return is_integer(ty) || is_flonum(ty); } +bool is_compatible(Type *t1, Type *t2) { + if (t1 == t2) + return true; + + if (t1->origin) + return is_compatible(t1->origin, t2); + + if (t2->origin) + return is_compatible(t1, t2->origin); + + if (t1->kind != t2->kind) + return false; + + switch (t1->kind) { + case TY_CHAR: + case TY_SHORT: + case TY_INT: + case TY_LONG: + return t1->is_unsigned == t2->is_unsigned; + case TY_FLOAT: + case TY_DOUBLE: + return true; + case TY_PTR: + return is_compatible(t1->base, t2->base); + case TY_FUNC: { + if (!is_compatible(t1->return_ty, t2->return_ty)) + return false; + if (t1->is_variadic != t2->is_variadic) + return false; + + Type *p1 = t1->params; + Type *p2 = t2->params; + for (; p1 && p2; p1 = p1->next, p2 = p2->next) + if (!is_compatible(p1, p2)) + return false; + return p1 == NULL && p2 == NULL; + } + case TY_ARRAY: + if (!is_compatible(t1->base, t2->base)) + return false; + return t1->array_len < 0 && t2->array_len < 0 && + t1->array_len == t2->array_len; + } + return false; +} + Type *copy_type(Type *ty) { Type *ret = calloc(1, sizeof(Type)); *ret = *ty; + ret->origin = ty; return ret; } -- GitLab