diff options
author | Christopher Li <sparse@chrisli.org> | 2012-01-02 13:47:21 -0800 |
---|---|---|
committer | Christopher Li <sparse@chrisli.org> | 2012-01-02 14:01:37 -0800 |
commit | e68f464686c2fcec987d76ecc3da061db7f784e9 (patch) | |
tree | c162ff53496e76db7e39820db3f4dbb3fd1bf5ca | |
parent | 65a4e2fc656aa6e99604358056d8599a1823a8bc (diff) | |
parent | 65be24b8ddf54164ff25febfd3d5f9c26ae3877d (diff) | |
download | sparse-e68f464686c2fcec987d76ecc3da061db7f784e9.tar.gz |
Merge branch 'sparse-llvm' of git://github.com/penberg/sparse-llvm.git
'master' branch of git://github.com/penberg/sparse-llvm.git
Revert "sparse: Bump up sizeof(_Bool) to 8 bits"
sparse, llvm: Add test case for <stdbool.h> type
sparse, llvm: Use LLVMInt1Type() in sym_basetype_type()
sparse, llvm: Don't fail the build if LLVM is too old
Merge pull request #6 from jgarzik/hacks
sparse, llvm: add loop testcase
sparse, llvm: Fix loops, by properly handling OP_PHI forward references
sparse, llvm: FP comparison op code generation
sparse, llvm: Simplify comparison op code generation
sparse, llvm: More comparison ops code generation
sparse, llvm: OP_SET_B and OP_SET_A code generation
sparse, llvm: Pointer cast code generation
sparse, llvm: Make llc output to stdout in sparsec
sparse, llvm: Fix 'extern' symbol code generation
sparse, llvm: Fix symbol initializer code generation
sparse, llvm: Function pointer code generation
sparse, llvm: Make 'sparsec' error handling more robust
sparse, llvm: Add support for union types
sparse, llvm: Add support for array types
sparse, llvm: Fix symbol_type() for bitfields and enums
sparse, llvm: Fix struct code generation
sparse, llvm: Use new LLVM type system API for structs
sparse, llvm: Fix 'void *' pointer code generation
sparse, llvm: Add support for logical ops
sparse: Bump up sizeof(_Bool) to 8 bits
sparse, llvm: Add support for symbol initializers
sparse, llvm: Add support for struct types
sparse, llvm: Fix code generation for 'long double' data type
Merge pull request #4 from jgarzik/hacks
sparse, llvm: support OP_STORE
sparse, llvm: move OP_COPY support to separate function. Add FP support.
sparse, llvm: store module-local functions on function reference list
llvm, sparse: Fix symbol_is_fp_type() goof
Merge branch 'master' of github.com:penberg/sparse-llvm
Merge pull request #3 from jgarzik/hacks
sparse, llvm: Fix pseudo_type() for PSEUDO_ARG
sparse, llvm: create helper for obtaining instruction's type
sparse, llvm: Fix code generation for casts
Revert "sparse, llvm: Don't redefine module local functions"
sparse, llvm: Don't redefine module local functions
sparse, llvm: Fix PSEUDO_OP code generation
sparse, llvm: Improve sparsec front-end
sparse, llvm: Fix OP_CAST to use zero-extend
sparse, llvm: Cleanup output_data()
sparse, llvm: Code generation for string constants
sparse, llvm: Warn the user when we fall back to GCC
Limit usage of g++ to llvm related programs.
Merge pull request #2 from jgarzik/hacks
sparse, llvm: move OP_CAST code to separate func. support FP casts.
cse: update PHI users when throwing away an instruction
cse: treat PHI-nodes as other instructions
Merge pull request #1 from jgarzik/hacks
sparse, llvm: move OP_PHI code from switch statement to separate function
sparse, llvm: implement OP_CALL
sparse, llvm: replace FIXME comment with assert(), following existing style
sparse-llvm OP_PHISOURCE: replace copy with target=src pointer operation
sparse, llvm: Kill debugging code
sparse, llvm: Kill ifdef'd unssa() call
sparse, llvm: Bitwise not operator codegen
sparse, llvm: Reorganize code generation tests
sparse, llvm: Floating point support for binops
sparse-llvm: OP_LOAD
sparse-llvm: OP_SWITCH
sparse-llvm: OP_SEL
sparse, llvm: if-else code generation
sparse, llvm: Implement OP_CAST
sparse, llvm: Move binop tests to validation/backend
sparse, llvm: Implement some binary comparison ops
sparse, llvm: Add support for more binary ops
sparse, llvm: Implement OP_ADD
sparse, llvm: Add output_op_binary() stub
sparse, llvm: Introduce 'struct function' to clean up code
sparse, llvm: Add support for OP_RET/PSEUDO_ARG
sparse, llvm: OP_RET/PSEUDO_VAL code generation
sparse, llvm: Add switch statement to output_insn()
llvm, sparse: Separate entry and exit basic blocks
sparse, llvm: Fix 'sparsec' when it's not in PATH
sparse, llvm: Fix global variable initialization
sparse, llvm: Fix assert() in sparse code
sparse, llvm: Initial commit
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 26 | ||||
-rw-r--r-- | linearize.h | 2 | ||||
-rw-r--r-- | sparse-llvm.c | 1241 | ||||
-rwxr-xr-x | sparsec | 52 | ||||
-rw-r--r-- | validation/backend/arithmetic-ops.c | 94 | ||||
-rw-r--r-- | validation/backend/array.c | 6 | ||||
-rw-r--r-- | validation/backend/bitwise-ops.c | 64 | ||||
-rw-r--r-- | validation/backend/bool-test.c | 9 | ||||
-rw-r--r-- | validation/backend/cast.c | 47 | ||||
-rw-r--r-- | validation/backend/cmp-ops.c | 84 | ||||
-rw-r--r-- | validation/backend/function-ptr.c | 11 | ||||
-rw-r--r-- | validation/backend/hello.c | 13 | ||||
-rw-r--r-- | validation/backend/logical-ops.c | 24 | ||||
-rw-r--r-- | validation/backend/loop.c | 21 | ||||
-rw-r--r-- | validation/backend/ptrcast.c | 9 | ||||
-rw-r--r-- | validation/backend/struct.c | 19 | ||||
-rw-r--r-- | validation/backend/union.c | 12 |
18 files changed, 1734 insertions, 1 deletions
@@ -23,6 +23,7 @@ example test-unssa ctags c2xml +sparse-llvm # tags tags @@ -7,6 +7,7 @@ CC = gcc CFLAGS = -O2 -finline-functions -fno-strict-aliasing -g CFLAGS += -Wall -Wwrite-strings LDFLAGS += -g +LD = gcc AR = ar ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) @@ -21,6 +22,9 @@ HAVE_GCC_DEP:=$(shell touch .gcc-test.c && \ $(CC) -c -Wp,-MD,.gcc-test.d .gcc-test.c 2>/dev/null && \ echo 'yes'; rm -f .gcc-test.d .gcc-test.o .gcc-test.c) HAVE_GTK2:=$(shell pkg-config --exists gtk+-2.0 2>/dev/null && echo 'yes') +HAVE_LLVM:=$(shell llvm-config --version >/dev/null 2>&1 && echo 'yes') +HAVE_LLVM_VERSION:=$(shell llvm-config --version | grep "^[3-9].*" >/dev/null 2>&1 && echo yes) +LLVM_VERSION=$(shell llvm-config --version) GCC_BASE = $(shell $(CC) --print-file-name=) BASIC_CFLAGS = -DGCC_BASE=\"$(GCC_BASE)\" @@ -63,6 +67,26 @@ else $(warning Your system does not have libgtk2, disabling test-inspect) endif +ifneq ($(HAVE_LLVM),yes) +$(warning Your system does not have llvm, disabling sparse-llvm) +else +ifneq ($(HAVE_LLVM_VERSION),yes) +$(warning LLVM 3.0 or later required. Your system has version $(LLVM_VERSION) installed.) +HAVE_LLVM=no +else +LLVM_PROGS := sparse-llvm +$(LLVM_PROGS): LD := g++ +LDFLAGS += $(shell llvm-config --ldflags) +LLVM_CFLAGS := $(shell llvm-config --cflags | sed -e "s/-DNDEBUG//g") +LLVM_LIBS := $(shell llvm-config --libs) +PROGRAMS += $(LLVM_PROGS) +INST_PROGRAMS += sparse-llvm sparsec +sparse-llvm_EXTRA_DEPS := sparse-llvm.o +sparse-llvm.o $(sparse-llvm_EXTRA_DEPS): BASIC_CFLAGS += $(LLVM_CFLAGS) +sparse-llvm_EXTRA_OBJS := $(LLVM_LIBS) +endif +endif + LIB_H= token.h parse.h lib.h symbol.h scope.h expression.h target.h \ linearize.h bitmap.h ident-list.h compat.h flow.h allocate.h \ storage.h ptrlist.h dissect.h @@ -141,7 +165,7 @@ compile_EXTRA_DEPS = compile-i386.o $(foreach p,$(PROGRAMS),$(eval $(p): $($(p)_EXTRA_DEPS) $(LIBS))) $(PROGRAMS): % : %.o - $(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $^ $($@_EXTRA_OBJS) + $(QUIET_LINK)$(LD) $(LDFLAGS) -o $@ $^ $($@_EXTRA_OBJS) $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(AR) rcs $@ $(LIB_OBJS) diff --git a/linearize.h b/linearize.h index 50b36018..424ba970 100644 --- a/linearize.h +++ b/linearize.h @@ -38,6 +38,7 @@ struct pseudo { struct instruction *def; long long value; }; + void *priv; }; extern struct pseudo void_pseudo; @@ -231,6 +232,7 @@ struct basic_block { struct basic_block_list *children; /* destinations */ struct instruction_list *insns; /* Linear list of instructions */ struct pseudo_list *needs, *defines; + void *priv; }; static inline int is_branch_goto(struct instruction *br) diff --git a/sparse-llvm.c b/sparse-llvm.c new file mode 100644 index 00000000..a291a0d3 --- /dev/null +++ b/sparse-llvm.c @@ -0,0 +1,1241 @@ +/* + * Example usage: + * ./sparse-llvm hello.c | llc | as -o hello.o + */ + +#include <llvm-c/Core.h> +#include <llvm-c/BitWriter.h> + +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> + +#include "symbol.h" +#include "expression.h" +#include "linearize.h" +#include "flow.h" + +struct phi_fwd { + struct phi_fwd *next; + + LLVMValueRef phi; + pseudo_t pseudo; + bool resolved; +}; + +struct function { + LLVMBuilderRef builder; + LLVMTypeRef type; + LLVMValueRef fn; + LLVMModuleRef module; + + struct phi_fwd *fwd_list; +}; + +static inline bool symbol_is_fp_type(struct symbol *sym) +{ + if (!sym) + return false; + + return sym->ctype.base_type == &fp_type; +} + +static LLVMTypeRef symbol_type(LLVMModuleRef module, struct symbol *sym); + +static LLVMTypeRef func_return_type(LLVMModuleRef module, struct symbol *sym) +{ + return symbol_type(module, sym->ctype.base_type); +} + +static LLVMTypeRef sym_func_type(LLVMModuleRef module, struct symbol *sym) +{ + LLVMTypeRef *arg_type; + LLVMTypeRef func_type; + LLVMTypeRef ret_type; + struct symbol *arg; + int n_arg = 0; + + /* to avoid strangeness with varargs [for now], we build + * the function and type anew, for each call. This + * is probably wrong. We should look up the + * symbol declaration info. + */ + + ret_type = func_return_type(module, sym); + + /* count args, build argument type information */ + FOR_EACH_PTR(sym->arguments, arg) { + n_arg++; + } END_FOR_EACH_PTR(arg); + + arg_type = calloc(n_arg, sizeof(LLVMTypeRef)); + + int idx = 0; + FOR_EACH_PTR(sym->arguments, arg) { + struct symbol *arg_sym = arg->ctype.base_type; + + arg_type[idx++] = symbol_type(module, arg_sym); + } END_FOR_EACH_PTR(arg); + func_type = LLVMFunctionType(ret_type, arg_type, n_arg, + /* varargs? */ 0); + + return func_type; +} + +static LLVMTypeRef sym_array_type(LLVMModuleRef module, struct symbol *sym) +{ + LLVMTypeRef elem_type; + struct symbol *base_type; + + base_type = sym->ctype.base_type; + + elem_type = symbol_type(module, base_type); + if (!elem_type) + return NULL; + + return LLVMArrayType(elem_type, sym->bit_size / 8); +} + +#define MAX_STRUCT_MEMBERS 64 + +static LLVMTypeRef sym_struct_type(LLVMModuleRef module, struct symbol *sym) +{ + LLVMTypeRef elem_types[MAX_STRUCT_MEMBERS]; + struct symbol *member; + char buffer[256]; + LLVMTypeRef ret; + unsigned nr = 0; + + sprintf(buffer, "%.*s", sym->ident->len, sym->ident->name); + + ret = LLVMGetTypeByName(module, buffer); + if (ret) + return ret; + + ret = LLVMStructCreateNamed(LLVMGetGlobalContext(), buffer); + + FOR_EACH_PTR(sym->symbol_list, member) { + LLVMTypeRef member_type; + + assert(nr < MAX_STRUCT_MEMBERS); + + member_type = symbol_type(module, member); + + elem_types[nr++] = member_type; + } END_FOR_EACH_PTR(member); + + LLVMStructSetBody(ret, elem_types, nr, 0 /* packed? */); + return ret; +} + +static LLVMTypeRef sym_union_type(LLVMModuleRef module, struct symbol *sym) +{ + LLVMTypeRef elements; + unsigned union_size; + + /* + * There's no union support in the LLVM API so we treat unions as + * opaque structs. The downside is that we lose type information on the + * members but as LLVM doesn't care, neither do we. + */ + union_size = sym->bit_size / 8; + + elements = LLVMArrayType(LLVMInt8Type(), union_size); + + return LLVMStructType(&elements, 1, 0 /* packed? */); +} + +static LLVMTypeRef sym_ptr_type(LLVMModuleRef module, struct symbol *sym) +{ + LLVMTypeRef type = symbol_type(module, sym->ctype.base_type); + + return LLVMPointerType(type, 0); +} + +static LLVMTypeRef sym_basetype_type(struct symbol *sym) +{ + LLVMTypeRef ret = NULL; + + if (symbol_is_fp_type(sym)) { + switch (sym->bit_size) { + case 32: + ret = LLVMFloatType(); + break; + case 64: + ret = LLVMDoubleType(); + break; + case 80: + ret = LLVMX86FP80Type(); + break; + default: + die("invalid bit size %d for type %d", sym->bit_size, sym->type); + break; + } + } else { + switch (sym->bit_size) { + case 1: + ret = LLVMInt1Type(); + break; + case -1: /* 'void *' is treated like 'char *' */ + case 8: + ret = LLVMInt8Type(); + break; + case 16: + ret = LLVMInt16Type(); + break; + case 32: + ret = LLVMInt32Type(); + break; + case 64: + ret = LLVMInt64Type(); + break; + default: + die("invalid bit size %d for type %d", sym->bit_size, sym->type); + break; + } + } + + return ret; +} + +static LLVMTypeRef symbol_type(LLVMModuleRef module, struct symbol *sym) +{ + LLVMTypeRef ret = NULL; + + switch (sym->type) { + case SYM_BITFIELD: + case SYM_ENUM: + case SYM_NODE: + ret = symbol_type(module, sym->ctype.base_type); + break; + case SYM_BASETYPE: + ret = sym_basetype_type(sym); + break; + case SYM_PTR: + ret = sym_ptr_type(module, sym); + break; + case SYM_UNION: + ret = sym_union_type(module, sym); + break; + case SYM_STRUCT: + ret = sym_struct_type(module, sym); + break; + case SYM_ARRAY: + ret = sym_array_type(module, sym); + break; + case SYM_FN: + ret = sym_func_type(module, sym); + break; + default: + assert(0); + } + return ret; +} + +static LLVMTypeRef insn_symbol_type(LLVMModuleRef module, struct instruction *insn) +{ + if (insn->type) + return symbol_type(module, insn->type); + + switch (insn->size) { + case 8: return LLVMInt8Type(); + case 16: return LLVMInt16Type(); + case 32: return LLVMInt32Type(); + case 64: return LLVMInt64Type(); + + default: + die("invalid bit size %d", insn->size); + break; + } + + return NULL; /* not reached */ +} + +static LLVMLinkage data_linkage(struct symbol *sym) +{ + if (sym->ctype.modifiers & MOD_STATIC) + return LLVMPrivateLinkage; + + return LLVMExternalLinkage; +} + +static LLVMLinkage function_linkage(struct symbol *sym) +{ + if (sym->ctype.modifiers & MOD_STATIC) + return LLVMInternalLinkage; + + return LLVMExternalLinkage; +} + +#define MAX_PSEUDO_NAME 64 + +static void pseudo_name(pseudo_t pseudo, char *buf) +{ + switch (pseudo->type) { + case PSEUDO_REG: + snprintf(buf, MAX_PSEUDO_NAME, "R%d", pseudo->nr); + break; + case PSEUDO_SYM: + assert(0); + break; + case PSEUDO_VAL: + assert(0); + break; + case PSEUDO_ARG: { + assert(0); + break; + } + case PSEUDO_PHI: + snprintf(buf, MAX_PSEUDO_NAME, "PHI%d", pseudo->nr); + break; + default: + assert(0); + } +} + +static LLVMValueRef pseudo_to_value(struct function *fn, struct instruction *insn, pseudo_t pseudo) +{ + LLVMValueRef result = NULL; + + switch (pseudo->type) { + case PSEUDO_REG: + result = pseudo->priv; + break; + case PSEUDO_SYM: { + struct symbol *sym = pseudo->sym; + struct expression *expr; + + assert(sym->bb_target == NULL); + assert(sym->ident == NULL); + + expr = sym->initializer; + if (expr) { + switch (expr->type) { + case EXPR_STRING: { + const char *s = expr->string->data; + LLVMValueRef indices[] = { LLVMConstInt(LLVMInt64Type(), 0, 0), LLVMConstInt(LLVMInt64Type(), 0, 0) }; + LLVMValueRef data; + + data = LLVMAddGlobal(fn->module, LLVMArrayType(LLVMInt8Type(), strlen(s) + 1), ".str"); + LLVMSetLinkage(data, LLVMPrivateLinkage); + LLVMSetGlobalConstant(data, 1); + LLVMSetInitializer(data, LLVMConstString(strdup(s), strlen(s) + 1, true)); + + result = LLVMConstGEP(data, indices, ARRAY_SIZE(indices)); + break; + } + default: + assert(0); + } + } + break; + } + case PSEUDO_VAL: + result = LLVMConstInt(insn_symbol_type(fn->module, insn), pseudo->value, 1); + break; + case PSEUDO_ARG: { + result = LLVMGetParam(fn->fn, pseudo->nr - 1); + break; + } + case PSEUDO_PHI: + result = pseudo->priv; + break; + case PSEUDO_VOID: + result = NULL; + break; + default: + assert(0); + } + + return result; +} + +static LLVMTypeRef pseudo_type(struct function *fn, struct instruction *insn, pseudo_t pseudo) +{ + LLVMValueRef v; + LLVMTypeRef result = NULL; + + if (pseudo->priv) { + v = pseudo->priv; + return LLVMTypeOf(v); + } + + switch (pseudo->type) { + case PSEUDO_REG: + result = symbol_type(fn->module, pseudo->def->type); + break; + case PSEUDO_SYM: { + struct symbol *sym = pseudo->sym; + struct expression *expr; + + assert(sym->bb_target == NULL); + assert(sym->ident == NULL); + + expr = sym->initializer; + if (expr) { + switch (expr->type) { + case EXPR_STRING: + result = LLVMPointerType(LLVMInt8Type(), 0); + break; + default: + assert(0); + } + } + break; + } + case PSEUDO_VAL: + result = insn_symbol_type(fn->module, insn); + break; + case PSEUDO_ARG: + result = LLVMTypeOf(LLVMGetParam(fn->fn, pseudo->nr - 1)); + break; + case PSEUDO_PHI: + assert(0); + break; + case PSEUDO_VOID: + result = LLVMVoidType(); + break; + default: + assert(0); + } + + return result; +} + +static LLVMRealPredicate translate_fop(int opcode) +{ + static const LLVMRealPredicate trans_tbl[] = { + [OP_SET_EQ] = LLVMRealOEQ, + [OP_SET_NE] = LLVMRealUNE, + [OP_SET_LE] = LLVMRealOLE, + [OP_SET_GE] = LLVMRealOGE, + [OP_SET_LT] = LLVMRealOLT, + [OP_SET_GT] = LLVMRealOGT, + /* Are these used with FP? */ + [OP_SET_B] = LLVMRealOLT, + [OP_SET_A] = LLVMRealOGT, + [OP_SET_BE] = LLVMRealOLE, + [OP_SET_AE] = LLVMRealOGE, + }; + + return trans_tbl[opcode]; +} + +static LLVMIntPredicate translate_op(int opcode) +{ + static const LLVMIntPredicate trans_tbl[] = { + [OP_SET_EQ] = LLVMIntEQ, + [OP_SET_NE] = LLVMIntNE, + [OP_SET_LE] = LLVMIntSLE, + [OP_SET_GE] = LLVMIntSGE, + [OP_SET_LT] = LLVMIntSLT, + [OP_SET_GT] = LLVMIntSGT, + [OP_SET_B] = LLVMIntULT, + [OP_SET_A] = LLVMIntUGT, + [OP_SET_BE] = LLVMIntULE, + [OP_SET_AE] = LLVMIntUGE, + }; + + return trans_tbl[opcode]; +} + +static void output_op_binary(struct function *fn, struct instruction *insn) +{ + LLVMValueRef lhs, rhs, target; + char target_name[64]; + + lhs = pseudo_to_value(fn, insn, insn->src1); + + rhs = pseudo_to_value(fn, insn, insn->src2); + + pseudo_name(insn->target, target_name); + + switch (insn->opcode) { + /* Binary */ + case OP_ADD: + if (symbol_is_fp_type(insn->type)) + target = LLVMBuildFAdd(fn->builder, lhs, rhs, target_name); + else + target = LLVMBuildAdd(fn->builder, lhs, rhs, target_name); + break; + case OP_SUB: + if (symbol_is_fp_type(insn->type)) + target = LLVMBuildFSub(fn->builder, lhs, rhs, target_name); + else + target = LLVMBuildSub(fn->builder, lhs, rhs, target_name); + break; + case OP_MULU: + if (symbol_is_fp_type(insn->type)) + target = LLVMBuildFMul(fn->builder, lhs, rhs, target_name); + else + target = LLVMBuildMul(fn->builder, lhs, rhs, target_name); + break; + case OP_MULS: + assert(!symbol_is_fp_type(insn->type)); + target = LLVMBuildMul(fn->builder, lhs, rhs, target_name); + break; + case OP_DIVU: + if (symbol_is_fp_type(insn->type)) + target = LLVMBuildFDiv(fn->builder, lhs, rhs, target_name); + else + target = LLVMBuildUDiv(fn->builder, lhs, rhs, target_name); + break; + case OP_DIVS: + assert(!symbol_is_fp_type(insn->type)); + target = LLVMBuildSDiv(fn->builder, lhs, rhs, target_name); + break; + case OP_MODU: + assert(!symbol_is_fp_type(insn->type)); + target = LLVMBuildURem(fn->builder, lhs, rhs, target_name); + break; + case OP_MODS: + assert(!symbol_is_fp_type(insn->type)); + target = LLVMBuildSRem(fn->builder, lhs, rhs, target_name); + break; + case OP_SHL: + assert(!symbol_is_fp_type(insn->type)); + target = LLVMBuildShl(fn->builder, lhs, rhs, target_name); + break; + case OP_LSR: + assert(!symbol_is_fp_type(insn->type)); + target = LLVMBuildLShr(fn->builder, lhs, rhs, target_name); + break; + case OP_ASR: + assert(!symbol_is_fp_type(insn->type)); + target = LLVMBuildAShr(fn->builder, lhs, rhs, target_name); + break; + + /* Logical */ + case OP_AND: + assert(!symbol_is_fp_type(insn->type)); + target = LLVMBuildAnd(fn->builder, lhs, rhs, target_name); + break; + case OP_OR: + assert(!symbol_is_fp_type(insn->type)); + target = LLVMBuildOr(fn->builder, lhs, rhs, target_name); + break; + case OP_XOR: + assert(!symbol_is_fp_type(insn->type)); + target = LLVMBuildXor(fn->builder, lhs, rhs, target_name); + break; + case OP_AND_BOOL: { + LLVMValueRef x, y; + + assert(!symbol_is_fp_type(insn->type)); + + y = LLVMBuildICmp(fn->builder, LLVMIntNE, lhs, LLVMConstInt(LLVMTypeOf(lhs), 0, 0), "y"); + x = LLVMBuildICmp(fn->builder, LLVMIntNE, rhs, LLVMConstInt(LLVMTypeOf(rhs), 0, 0), "x"); + + target = LLVMBuildAnd(fn->builder, y, x, target_name); + break; + } + case OP_OR_BOOL: { + LLVMValueRef tmp; + + assert(!symbol_is_fp_type(insn->type)); + + tmp = LLVMBuildOr(fn->builder, rhs, lhs, "tmp"); + + target = LLVMBuildICmp(fn->builder, LLVMIntNE, tmp, LLVMConstInt(LLVMTypeOf(tmp), 0, 0), target_name); + break; + } + + /* Binary comparison */ + case OP_BINCMP ... OP_BINCMP_END: { + if (LLVMGetTypeKind(LLVMTypeOf(lhs)) == LLVMIntegerTypeKind) { + LLVMIntPredicate op = translate_op(insn->opcode); + + target = LLVMBuildICmp(fn->builder, op, lhs, rhs, target_name); + } else { + LLVMRealPredicate op = translate_fop(insn->opcode); + + target = LLVMBuildFCmp(fn->builder, op, lhs, rhs, target_name); + } + break; + } + default: + assert(0); + break; + } + + insn->target->priv = target; +} + +static void output_op_ret(struct function *fn, struct instruction *insn) +{ + pseudo_t pseudo = insn->src; + + if (pseudo && pseudo != VOID) { + LLVMValueRef result = pseudo_to_value(fn, insn, pseudo); + + LLVMBuildRet(fn->builder, result); + } else + LLVMBuildRetVoid(fn->builder); +} + +static void output_op_load(struct function *fn, struct instruction *insn) +{ + LLVMTypeRef int_type; + LLVMValueRef src_p, src_i, ofs_i, addr_i, addr, target; + + /* int type large enough to hold a pointer */ + int_type = LLVMIntType(bits_in_pointer); + + /* convert to integer, add src + offset */ + src_p = pseudo_to_value(fn, insn, insn->src); + src_i = LLVMBuildPtrToInt(fn->builder, src_p, int_type, "src_i"); + + ofs_i = LLVMConstInt(int_type, insn->offset, 0); + addr_i = LLVMBuildAdd(fn->builder, src_i, ofs_i, "addr_i"); + + /* convert address back to pointer */ + addr = LLVMBuildIntToPtr(fn->builder, addr_i, + LLVMPointerType(int_type, 0), "addr"); + + /* perform load */ + target = LLVMBuildLoad(fn->builder, addr, "load_target"); + + insn->target->priv = target; +} + +static void output_op_store(struct function *fn, struct instruction *insn) +{ + LLVMTypeRef int_type; + LLVMValueRef src_p, src_i, ofs_i, addr_i, addr, target, target_in; + + /* int type large enough to hold a pointer */ + int_type = LLVMIntType(bits_in_pointer); + + /* convert to integer, add src + offset */ + src_p = pseudo_to_value(fn, insn, insn->src); + src_i = LLVMBuildPtrToInt(fn->builder, src_p, int_type, "src_i"); + + ofs_i = LLVMConstInt(int_type, insn->offset, 0); + addr_i = LLVMBuildAdd(fn->builder, src_i, ofs_i, "addr_i"); + + /* convert address back to pointer */ + addr = LLVMBuildIntToPtr(fn->builder, addr_i, + LLVMPointerType(int_type, 0), "addr"); + + target_in = pseudo_to_value(fn, insn, insn->target); + + /* perform store */ + target = LLVMBuildStore(fn->builder, target_in, addr); + + insn->target->priv = target; +} + +static void output_op_br(struct function *fn, struct instruction *br) +{ + if (br->cond) { + LLVMValueRef cond = pseudo_to_value(fn, br, br->cond); + + LLVMBuildCondBr(fn->builder, cond, + br->bb_true->priv, + br->bb_false->priv); + } else + LLVMBuildBr(fn->builder, + br->bb_true ? br->bb_true->priv : + br->bb_false->priv); +} + +static void output_op_sel(struct function *fn, struct instruction *insn) +{ + LLVMValueRef target, src1, src2, src3; + + src1 = pseudo_to_value(fn, insn, insn->src1); + src2 = pseudo_to_value(fn, insn, insn->src2); + src3 = pseudo_to_value(fn, insn, insn->src3); + + target = LLVMBuildSelect(fn->builder, src1, src2, src3, "select"); + + insn->target->priv = target; +} + +static void output_op_switch(struct function *fn, struct instruction *insn) +{ + LLVMValueRef sw_val, target; + struct basic_block *def = NULL; + struct multijmp *jmp; + int n_jmp = 0; + + FOR_EACH_PTR(insn->multijmp_list, jmp) { + if (jmp->begin == jmp->end) { /* case N */ + n_jmp++; + } else if (jmp->begin < jmp->end) { /* case M..N */ + assert(0); + } else /* default case */ + def = jmp->target; + } END_FOR_EACH_PTR(jmp); + + sw_val = pseudo_to_value(fn, insn, insn->target); + target = LLVMBuildSwitch(fn->builder, sw_val, + def ? def->priv : NULL, n_jmp); + + FOR_EACH_PTR(insn->multijmp_list, jmp) { + if (jmp->begin == jmp->end) { /* case N */ + LLVMAddCase(target, + LLVMConstInt(LLVMInt32Type(), jmp->begin, 0), + jmp->target->priv); + } else if (jmp->begin < jmp->end) { /* case M..N */ + assert(0); + } + } END_FOR_EACH_PTR(jmp); + + insn->target->priv = target; +} + +struct llfunc { + char name[256]; /* wasteful */ + LLVMValueRef func; +}; + +DECLARE_ALLOCATOR(llfunc); +DECLARE_PTR_LIST(llfunc_list, struct llfunc); +ALLOCATOR(llfunc, "llfuncs"); + +static struct local_module { + struct llfunc_list *llfunc_list; +} mi; + +static LLVMTypeRef get_func_type(struct function *fn, struct instruction *insn) +{ + struct symbol *sym = insn->func->sym; + char buffer[256]; + LLVMTypeRef func_type, ret_type; + struct pseudo *arg; + int n_arg = 0; + LLVMTypeRef *arg_type; + + if (sym->ident) + sprintf(buffer, "%.*s", sym->ident->len, sym->ident->name); + else + sprintf(buffer, "<anon sym %p>", sym); + + /* VERIFY: is this correct, for functions? */ + func_type = LLVMGetTypeByName(fn->module, buffer); + if (func_type) + return func_type; + + /* to avoid strangeness with varargs [for now], we build + * the function and type anew, for each call. This + * is probably wrong. We should look up the + * symbol declaration info. + */ + + /* build return type */ + if (insn->target && insn->target != VOID) + ret_type = pseudo_type(fn, insn, insn->target); + else + ret_type = LLVMVoidType(); + + /* count args, build argument type information */ + FOR_EACH_PTR(insn->arguments, arg) { + n_arg++; + } END_FOR_EACH_PTR(arg); + + arg_type = calloc(n_arg, sizeof(LLVMTypeRef)); + + int idx = 0; + FOR_EACH_PTR(insn->arguments, arg) { + arg_type[idx++] = pseudo_type(fn, insn, arg); + } END_FOR_EACH_PTR(arg); + + func_type = LLVMFunctionType(ret_type, arg_type, n_arg, + /* varargs? */ 0); + + return func_type; +} + +static LLVMValueRef get_function(struct function *fn, struct instruction *insn) +{ + struct symbol *sym = insn->func->sym; + char buffer[256]; + LLVMValueRef func; + struct llfunc *f; + + if (sym->ident) + sprintf(buffer, "%.*s", sym->ident->len, sym->ident->name); + else + sprintf(buffer, "<anon sym %p>", sym); + + + /* search for pre-built function type definition */ + FOR_EACH_PTR(mi.llfunc_list, f) { + if (!strcmp(f->name, buffer)) + return f->func; /* found match; return */ + } END_FOR_EACH_PTR(f); + + /* build function type definition */ + LLVMTypeRef func_type = get_func_type(fn, insn); + + func = LLVMAddFunction(fn->module, buffer, func_type); + + /* store built function on list, for later referencing */ + f = calloc(1, sizeof(*f)); + strncpy(f->name, buffer, sizeof(f->name) - 1); + f->func = func; + + add_ptr_list(&mi.llfunc_list, f); + + return func; +} + +static void output_op_call(struct function *fn, struct instruction *insn) +{ + LLVMValueRef target, func; + int n_arg = 0, i; + struct pseudo *arg; + LLVMValueRef *args; + + FOR_EACH_PTR(insn->arguments, arg) { + n_arg++; + } END_FOR_EACH_PTR(arg); + + args = calloc(n_arg, sizeof(LLVMValueRef)); + + i = 0; + FOR_EACH_PTR(insn->arguments, arg) { + args[i++] = pseudo_to_value(fn, insn, arg); + } END_FOR_EACH_PTR(arg); + + func = get_function(fn, insn); + target = LLVMBuildCall(fn->builder, func, args, n_arg, ""); + + insn->target->priv = target; +} + +static void store_phi_fwd(struct function *fn, LLVMValueRef phi, + pseudo_t pseudo) +{ + struct phi_fwd *fwd; + + fwd = calloc(1, sizeof(*fwd)); + fwd->phi = phi; + fwd->pseudo = pseudo; + + /* append fwd ref to function-wide list */ + if (!fn->fwd_list) + fn->fwd_list = fwd; + else { + struct phi_fwd *last = fn->fwd_list; + + while (last->next) + last = last->next; + last->next = fwd; + } +} + +static void output_phi_fwd(struct function *fn, pseudo_t pseudo, LLVMValueRef v) +{ + struct phi_fwd *fwd = fn->fwd_list; + + while (fwd) { + struct phi_fwd *tmp; + + tmp = fwd; + fwd = fwd->next; + + if (tmp->pseudo == pseudo && !tmp->resolved) { + LLVMValueRef phi_vals[1]; + LLVMBasicBlockRef phi_blks[1]; + + phi_vals[0] = v; + phi_blks[0] = pseudo->def->bb->priv; + + LLVMAddIncoming(tmp->phi, phi_vals, phi_blks, 1); + + tmp->resolved = true; + } + } +} + +static void output_op_phisrc(struct function *fn, struct instruction *insn) +{ + LLVMValueRef v; + + assert(insn->target->priv == NULL); + + /* target = src */ + v = pseudo_to_value(fn, insn, insn->phi_src); + insn->target->priv = v; + + assert(insn->target->priv != NULL); + + /* resolve forward references to this phi source, if present */ + output_phi_fwd(fn, insn->target, v); +} + +static void output_op_phi(struct function *fn, struct instruction *insn) +{ + pseudo_t phi; + LLVMValueRef target; + + target = LLVMBuildPhi(fn->builder, insn_symbol_type(fn->module, insn), + "phi"); + int pll = 0; + FOR_EACH_PTR(insn->phi_list, phi) { + if (pseudo_to_value(fn, insn, phi)) /* skip VOID, fwd refs*/ + pll++; + } END_FOR_EACH_PTR(phi); + + LLVMValueRef *phi_vals = calloc(pll, sizeof(LLVMValueRef)); + LLVMBasicBlockRef *phi_blks = calloc(pll, sizeof(LLVMBasicBlockRef)); + + int idx = 0; + FOR_EACH_PTR(insn->phi_list, phi) { + LLVMValueRef v; + + v = pseudo_to_value(fn, insn, phi); + if (v) { /* skip VOID, fwd refs */ + phi_vals[idx] = v; + phi_blks[idx] = phi->def->bb->priv; + idx++; + } + else if (phi->type == PSEUDO_PHI) /* fwd ref */ + store_phi_fwd(fn, target, phi); + } END_FOR_EACH_PTR(phi); + + LLVMAddIncoming(target, phi_vals, phi_blks, pll); + + insn->target->priv = target; +} + +static void output_op_ptrcast(struct function *fn, struct instruction *insn) +{ + LLVMValueRef src, target; + char target_name[64]; + + src = insn->src->priv; + if (!src) + src = pseudo_to_value(fn, insn, insn->src); + + pseudo_name(insn->target, target_name); + + assert(!symbol_is_fp_type(insn->type)); + + target = LLVMBuildBitCast(fn->builder, src, insn_symbol_type(fn->module, insn), target_name); + + insn->target->priv = target; +} + +static void output_op_cast(struct function *fn, struct instruction *insn, LLVMOpcode op) +{ + LLVMValueRef src, target; + char target_name[64]; + + src = insn->src->priv; + if (!src) + src = pseudo_to_value(fn, insn, insn->src); + + pseudo_name(insn->target, target_name); + + assert(!symbol_is_fp_type(insn->type)); + + if (insn->size < LLVMGetIntTypeWidth(LLVMTypeOf(src))) + target = LLVMBuildTrunc(fn->builder, src, insn_symbol_type(fn->module, insn), target_name); + else + target = LLVMBuildCast(fn->builder, op, src, insn_symbol_type(fn->module, insn), target_name); + + insn->target->priv = target; +} + +static void output_op_copy(struct function *fn, struct instruction *insn, + pseudo_t pseudo) +{ + LLVMValueRef src, target; + LLVMTypeRef const_type; + char target_name[64]; + + pseudo_name(insn->target, target_name); + src = pseudo_to_value(fn, insn, pseudo); + const_type = insn_symbol_type(fn->module, insn); + + /* + * This is nothing more than 'target = src' + * + * TODO: find a better way to provide an identity function, + * than using "X + 0" simply to produce a new LLVM pseudo + */ + + if (symbol_is_fp_type(insn->type)) + target = LLVMBuildFAdd(fn->builder, src, + LLVMConstReal(const_type, 0.0), target_name); + else + target = LLVMBuildAdd(fn->builder, src, + LLVMConstInt(const_type, 0, 0), target_name); + + insn->target->priv = target; +} + +static void output_insn(struct function *fn, struct instruction *insn) +{ + switch (insn->opcode) { + case OP_RET: + output_op_ret(fn, insn); + break; + case OP_BR: + output_op_br(fn, insn); + break; + case OP_SYMADDR: + assert(0); + break; + case OP_SETVAL: + assert(0); + break; + case OP_SWITCH: + output_op_switch(fn, insn); + break; + case OP_COMPUTEDGOTO: + assert(0); + break; + case OP_PHISOURCE: + output_op_phisrc(fn, insn); + break; + case OP_PHI: + output_op_phi(fn, insn); + break; + case OP_LOAD: + output_op_load(fn, insn); + break; + case OP_LNOP: + assert(0); + break; + case OP_STORE: + output_op_store(fn, insn); + break; + case OP_SNOP: + assert(0); + break; + case OP_INLINED_CALL: + assert(0); + break; + case OP_CALL: + output_op_call(fn, insn); + break; + case OP_CAST: + output_op_cast(fn, insn, LLVMZExt); + break; + case OP_SCAST: + output_op_cast(fn, insn, LLVMSExt); + break; + case OP_FPCAST: + assert(0); + break; + case OP_PTRCAST: + output_op_ptrcast(fn, insn); + break; + case OP_BINARY ... OP_BINARY_END: + case OP_BINCMP ... OP_BINCMP_END: + output_op_binary(fn, insn); + break; + case OP_SEL: + output_op_sel(fn, insn); + break; + case OP_SLICE: + assert(0); + break; + case OP_NOT: { + LLVMValueRef src, target; + char target_name[64]; + + src = pseudo_to_value(fn, insn, insn->src); + + pseudo_name(insn->target, target_name); + + target = LLVMBuildNot(fn->builder, src, target_name); + + insn->target->priv = target; + break; + } + case OP_NEG: + assert(0); + break; + case OP_CONTEXT: + assert(0); + break; + case OP_RANGE: + assert(0); + break; + case OP_NOP: + assert(0); + break; + case OP_DEATHNOTE: + assert(0); + break; + case OP_ASM: + assert(0); + break; + case OP_COPY: + output_op_copy(fn, insn, insn->src); + break; + default: + break; + } +} + +static void output_bb(struct function *fn, struct basic_block *bb, unsigned long generation) +{ + struct instruction *insn; + + bb->generation = generation; + + FOR_EACH_PTR(bb->insns, insn) { + if (!insn->bb) + continue; + + output_insn(fn, insn); + } + END_FOR_EACH_PTR(insn); +} + +#define MAX_ARGS 64 + +static void output_fn(LLVMModuleRef module, struct entrypoint *ep) +{ + unsigned long generation = ++bb_generation; + struct symbol *sym = ep->name; + struct symbol *base_type = sym->ctype.base_type; + struct symbol *ret_type = sym->ctype.base_type->ctype.base_type; + LLVMTypeRef arg_types[MAX_ARGS]; + LLVMTypeRef return_type; + struct function function = { .module = module }; + struct basic_block *bb; + struct symbol *arg; + const char *name; + int nr_args = 0; + struct llfunc *f; + + FOR_EACH_PTR(base_type->arguments, arg) { + struct symbol *arg_base_type = arg->ctype.base_type; + + arg_types[nr_args++] = symbol_type(module, arg_base_type); + } END_FOR_EACH_PTR(arg); + + name = show_ident(sym->ident); + + return_type = symbol_type(module, ret_type); + + function.type = LLVMFunctionType(return_type, arg_types, nr_args, 0); + + function.fn = LLVMAddFunction(module, name, function.type); + LLVMSetFunctionCallConv(function.fn, LLVMCCallConv); + + LLVMSetLinkage(function.fn, function_linkage(sym)); + + /* store built function on list, for later referencing */ + f = calloc(1, sizeof(*f)); + strncpy(f->name, name, sizeof(f->name) - 1); + f->func = function.fn; + + add_ptr_list(&mi.llfunc_list, f); + + function.builder = LLVMCreateBuilder(); + + static int nr_bb; + + FOR_EACH_PTR(ep->bbs, bb) { + if (bb->generation == generation) + continue; + + LLVMBasicBlockRef bbr; + char bbname[32]; + + sprintf(bbname, "L%d", nr_bb++); + bbr = LLVMAppendBasicBlock(function.fn, bbname); + + bb->priv = bbr; + } + END_FOR_EACH_PTR(bb); + + FOR_EACH_PTR(ep->bbs, bb) { + if (bb->generation == generation) + continue; + + LLVMPositionBuilderAtEnd(function.builder, bb->priv); + + output_bb(&function, bb, generation); + } + END_FOR_EACH_PTR(bb); +} + +static LLVMValueRef output_data(LLVMModuleRef module, struct symbol *sym) +{ + struct expression *initializer = sym->initializer; + LLVMValueRef initial_value; + LLVMValueRef data; + const char *name; + + if (initializer) { + switch (initializer->type) { + case EXPR_VALUE: + initial_value = LLVMConstInt(symbol_type(module, sym), initializer->value, 1); + break; + case EXPR_SYMBOL: { + struct symbol *sym = initializer->symbol; + + initial_value = LLVMGetNamedGlobal(module, show_ident(sym->ident)); + if (!initial_value) + initial_value = output_data(module, sym); + break; + } + default: + assert(0); + } + } else { + LLVMTypeRef type = symbol_type(module, sym); + + initial_value = LLVMConstNull(type); + } + + name = show_ident(sym->ident); + + data = LLVMAddGlobal(module, symbol_type(module, sym->ctype.base_type), name); + + LLVMSetLinkage(data, data_linkage(sym)); + + if (!(sym->ctype.modifiers & MOD_EXTERN)) + LLVMSetInitializer(data, initial_value); + + return data; +} + +static int compile(LLVMModuleRef module, struct symbol_list *list) +{ + struct symbol *sym; + + FOR_EACH_PTR(list, sym) { + struct entrypoint *ep; + expand_symbol(sym); + ep = linearize_symbol(sym); + if (ep) + output_fn(module, ep); + else + output_data(module, sym); + } + END_FOR_EACH_PTR(sym); + + return 0; +} + +int main(int argc, char **argv) +{ + struct string_list * filelist = NULL; + char *file; + + LLVMModuleRef module = LLVMModuleCreateWithName("sparse"); + + compile(module, sparse_initialize(argc, argv, &filelist)); + + FOR_EACH_PTR_NOTAG(filelist, file) { + compile(module, sparse(file)); + } END_FOR_EACH_PTR_NOTAG(file); + + LLVMWriteBitcodeToFD(module, STDOUT_FILENO, 0, 0); + + LLVMDisposeModule(module); + + return 0; +} diff --git a/sparsec b/sparsec new file mode 100755 index 00000000..9c90b305 --- /dev/null +++ b/sparsec @@ -0,0 +1,52 @@ +#!/bin/sh +# +# GCC compatible C compiler based on Sparse LLVM + +set +e + +SPARSEOPTS="" +DIRNAME=`dirname $0` + +NEED_LINK=1 + +if [ $# -eq 0 ]; then + echo "`basename $0`: no input files" + exit 1 +fi + +while [ $# -gt 0 ]; do + case $1 in + '-o') + OUTFILE=$2 + shift + ;; + '-c') + NEED_LINK=0 + ;; + *) + SPARSEOPTS="$SPARSEOPTS $1 " ;; + esac + shift +done + +TMPLLVM=`mktemp -t tmp.XXXXXX`".llvm" +TMPFILE=`mktemp -t tmp.XXXXXX`".o" + +$DIRNAME/sparse-llvm $SPARSEOPTS > $TMPLLVM + +llc -o - $TMPLLVM | as -o $TMPFILE + +if [ $NEED_LINK -eq 1 ]; then + if [ -z $OUTFILE ]; then + OUTFILE=a.out + fi + gcc $TMPFILE -o $OUTFILE +else + if [ -z $OUTFILE ]; then + echo "`basename $0`: no output file" + exit 1 + fi + mv $TMPFILE $OUTFILE +fi + +rm -f $TMPLLVM diff --git a/validation/backend/arithmetic-ops.c b/validation/backend/arithmetic-ops.c new file mode 100644 index 00000000..7c299d03 --- /dev/null +++ b/validation/backend/arithmetic-ops.c @@ -0,0 +1,94 @@ +static int add(int x, int y) +{ + return x + y; +} + +static unsigned int uadd(unsigned int x, unsigned int y) +{ + return x + y; +} + +static float fadd(float x, float y) +{ + return x + y; +} + +static double dadd(double x, double y) +{ + return x + y; +} + +static int sub(int x, int y) +{ + return x - y; +} + +static unsigned int usub(unsigned int x, unsigned int y) +{ + return x - y; +} + +static float fsub(float x, float y) +{ + return x - y; +} + +static double dsub(double x, double y) +{ + return x - y; +} + +static int mul(int x, int y) +{ + return x * y; +} + +static unsigned int umul(unsigned int x, unsigned int y) +{ + return x * y; +} + +static float fmul(float x, float y) +{ + return x * y; +} + +static double dmul(double x, double y) +{ + return x * y; +} + +static int div(int x, int y) +{ + return x / y; +} + +static unsigned int udiv(unsigned int x, unsigned int y) +{ + return x / y; +} + +static float fdiv(float x, float y) +{ + return x / y; +} + +static double ddiv(double x, double y) +{ + return x / y; +} + +static int mod(int x, int y) +{ + return x % y; +} + +static unsigned int umod(unsigned int x, unsigned int y) +{ + return x % y; +} + +/* + * check-name: Arithmetic operator code generation + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/array.c b/validation/backend/array.c new file mode 100644 index 00000000..bd3ec596 --- /dev/null +++ b/validation/backend/array.c @@ -0,0 +1,6 @@ +static char array[128]; + +/* + * check-name: Array code generation + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/bitwise-ops.c b/validation/backend/bitwise-ops.c new file mode 100644 index 00000000..659c7639 --- /dev/null +++ b/validation/backend/bitwise-ops.c @@ -0,0 +1,64 @@ +static int shl(int x, int y) +{ + return x << y; +} + +static unsigned int ushl(unsigned int x, unsigned int y) +{ + return x << y; +} + +static int shr(int x, int y) +{ + return x >> y; +} + +static unsigned int ushr(unsigned int x, unsigned int y) +{ + return x >> y; +} + +static int and(int x, int y) +{ + return x & y; +} + +static unsigned int uand(unsigned int x, unsigned int y) +{ + return x & y; +} + +static int or(int x, int y) +{ + return x | y; +} + +static unsigned int uor(unsigned int x, unsigned int y) +{ + return x | y; +} + +static int xor(int x, int y) +{ + return x ^ y; +} + +static unsigned int uxor(unsigned int x, unsigned int y) +{ + return x ^ y; +} + +static int not(int x) +{ + return ~x; +} + +static unsigned int unot(unsigned int x) +{ + return ~x; +} + +/* + * check-name: Bitwise operator code generation + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/bool-test.c b/validation/backend/bool-test.c new file mode 100644 index 00000000..a6f33a1a --- /dev/null +++ b/validation/backend/bool-test.c @@ -0,0 +1,9 @@ +static _Bool return_false(void) +{ + return 0; +} + +/* + * check-name: Boolean type code generation + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/cast.c b/validation/backend/cast.c new file mode 100644 index 00000000..3e677446 --- /dev/null +++ b/validation/backend/cast.c @@ -0,0 +1,47 @@ +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef long long longlong; +typedef unsigned long long ulonglong; + +#define DEFINE_CAST(from, to) \ + static to from##2##to(from x) { \ + return x; \ + } + +#define DEFINE_CASTS(from) \ + DEFINE_CAST(from, char) \ + DEFINE_CAST(from, uchar) \ + DEFINE_CAST(from, short) \ + DEFINE_CAST(from, ushort) \ + DEFINE_CAST(from, int) \ + DEFINE_CAST(from, uint) \ + DEFINE_CAST(from, long) \ + DEFINE_CAST(from, ulong) \ + DEFINE_CAST(from, longlong) \ + DEFINE_CAST(from, ulonglong) \ +/* + DEFINE_CAST(from, float) \ + DEFINE_CAST(from, double) +*/ + +DEFINE_CASTS(char) +DEFINE_CASTS(uchar) +DEFINE_CASTS(short) +DEFINE_CASTS(ushort) +DEFINE_CASTS(int) +DEFINE_CASTS(uint) +DEFINE_CASTS(long) +DEFINE_CASTS(ulong) +DEFINE_CASTS(longlong) +DEFINE_CASTS(ulonglong) +/* +DEFINE_CASTS(float) +DEFINE_CASTS(double) +*/ + +/* + * check-name: Cast code generation + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/cmp-ops.c b/validation/backend/cmp-ops.c new file mode 100644 index 00000000..a5f736d7 --- /dev/null +++ b/validation/backend/cmp-ops.c @@ -0,0 +1,84 @@ +static int sete(int x, int y) +{ + return x == y; +} + +static int setne(int x, int y) +{ + return x != y; +} + +static int setl(int x, int y) +{ + return x < y; +} + +static int setg(int x, int y) +{ + return x > y; +} + +static int setle(int x, int y) +{ + return x <= y; +} + +static int setge(int x, int y) +{ + return x >= y; +} + +static int setb(unsigned int x, unsigned int y) +{ + return x < y; +} + +static int seta(unsigned int x, unsigned int y) +{ + return x > y; +} + +static int setbe(unsigned int x, unsigned int y) +{ + return x <= y; +} + +static int setae(unsigned int x, unsigned int y) +{ + return x >= y; +} + +static int setfe(float x, float y) +{ + return x == y; +} + +static int setfne(float x, float y) +{ + return x != y; +} + +static int setfl(float x, float y) +{ + return x < y; +} + +static int setfg(float x, float y) +{ + return x > y; +} + +static int setfle(float x, float y) +{ + return x <= y; +} + +static int setfge(float x, float y) +{ + return x >= y; +} + +/* + * check-name: Comparison operator code generation + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/function-ptr.c b/validation/backend/function-ptr.c new file mode 100644 index 00000000..fc022b3c --- /dev/null +++ b/validation/backend/function-ptr.c @@ -0,0 +1,11 @@ +typedef int (*fn_t)(int x, int y); + +static int run(fn_t fn, int x, int y) +{ + return fn(x, y); +} + +/* + * check-name: Function pointer code generation + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/hello.c b/validation/backend/hello.c new file mode 100644 index 00000000..79905004 --- /dev/null +++ b/validation/backend/hello.c @@ -0,0 +1,13 @@ +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + puts("hello, world"); + + return 0; +} + +/* + * check-name: 'hello, world' code generation + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/logical-ops.c b/validation/backend/logical-ops.c new file mode 100644 index 00000000..8b2a6a85 --- /dev/null +++ b/validation/backend/logical-ops.c @@ -0,0 +1,24 @@ +static int and_bool(int x, int y) +{ + return x && y; +} + +static unsigned int uand_bool(unsigned int x, unsigned int y) +{ + return x && y; +} + +static int or_bool(int x, int y) +{ + return x || y; +} + +static unsigned int uor_bool(unsigned int x, unsigned int y) +{ + return x || y; +} + +/* + * check-name: Logical operator code generation + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/loop.c b/validation/backend/loop.c new file mode 100644 index 00000000..31054f52 --- /dev/null +++ b/validation/backend/loop.c @@ -0,0 +1,21 @@ + +extern int bar (int); + +extern int foo (int); + +int foo (int x) +{ + int y = 0; + + while (y < 1000) { + y += bar(x); + } + + return y; +} + + +/* + * check-name: Loops + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/ptrcast.c b/validation/backend/ptrcast.c new file mode 100644 index 00000000..46f8add8 --- /dev/null +++ b/validation/backend/ptrcast.c @@ -0,0 +1,9 @@ +static char *ptrcast(unsigned long *x) +{ + return (unsigned char *) x; +} + +/* + * check-name: Pointer cast code generation + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/struct.c b/validation/backend/struct.c new file mode 100644 index 00000000..1afaf2db --- /dev/null +++ b/validation/backend/struct.c @@ -0,0 +1,19 @@ +struct ctype { + int type; +}; + +struct symbol { + void *p; + const char *name; + struct ctype ctype; + struct symbol *next_id; +}; + +static struct symbol sym; +static struct symbol *sym_p; +static struct symbol *sym_q = &sym; + +/* + * check-name: Struct code generation + * check-command: ./sparsec -c $file -o tmp.o + */ diff --git a/validation/backend/union.c b/validation/backend/union.c new file mode 100644 index 00000000..e155f6ad --- /dev/null +++ b/validation/backend/union.c @@ -0,0 +1,12 @@ +union foo { + unsigned long x; + unsigned char y; + char buf[128]; +}; + +static union foo foo; + +/* + * check-name: Union code generation + * check-command: ./sparsec -c $file -o tmp.o + */ |