diff options
Diffstat (limited to 'usr/src')
641 files changed, 22940 insertions, 6169 deletions
diff --git a/usr/src/cmd/auditreduce/Makefile b/usr/src/cmd/auditreduce/Makefile index 9984ce57a6..1c39fe869f 100644 --- a/usr/src/cmd/auditreduce/Makefile +++ b/usr/src/cmd/auditreduce/Makefile @@ -24,7 +24,7 @@ # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright (c) 2018, Joyent, Inc. +# Copyright 2019 Joyent, Inc. TABLEDIR = ../praudit @@ -44,8 +44,8 @@ LDLIBS += -lnsl -lbsm -lscf -ltsol CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -_gcc=-Wno-parentheses -# false positives / need cleanup -SMOFF += indenting,no_if_block,strcpy_overflow +# false positive +SMOFF += strcpy_overflow .KEEP_STATE: diff --git a/usr/src/cmd/auditreduce/main.c b/usr/src/cmd/auditreduce/main.c index 3137bd9e9c..910b5aaf7e 100644 --- a/usr/src/cmd/auditreduce/main.c +++ b/usr/src/cmd/auditreduce/main.c @@ -25,6 +25,10 @@ */ /* + * Copyright 2019 Joyent, Inc. + */ + +/* * The Secure SunOS audit reduction tool - auditreduce. * Document SM0071 is the primary source of information on auditreduce. * @@ -247,11 +251,13 @@ mfork(audit_pcb_t *pcb, int nsp, int lo, int hi) * Convert descriptors to streams. */ if ((pcbn->pcb_fpr = fdopen(fildes[0], "r")) == NULL) { - perror(gettext("auditreduce: couldn't get read stream for pipe")); + perror(gettext("auditreduce: couldn't get read " + "stream for pipe")); return (-1); } if ((pcbn->pcb_fpw = fdopen(fildes[1], "w")) == NULL) { - perror(gettext("auditreduce: couldn't get write stream for pipe")); + perror(gettext("auditreduce: couldn't get " + "write stream for pipe")); return (-1); } if ((procno = fork()) == -1) { @@ -400,8 +406,10 @@ c_close(audit_pcb_t *pcb, int i) for (j = 0; j <= i; j++) { pcbt = &pcb->pcb_below[j]; if (fclose(pcbt->pcb_fpr) == EOF) { - if (!f_quiet) - perror(gettext("auditreduce: initial close on pipe failed")); + if (!f_quiet) { + perror(gettext("auditreduce: initial close " + "on pipe failed")); + } } /* * Free the buffer allocated to hold incoming records. @@ -426,8 +434,10 @@ static void p_close(audit_pcb_t *pcbn) { if (fclose(pcbn->pcb_fpw) == EOF) { - if (!f_quiet) - perror(gettext("auditreduce: close for write pipe failed")); + if (!f_quiet) { + perror(gettext("auditreduce: close for write " + "pipe failed")); + } } } diff --git a/usr/src/lib/udapl/libdat/Makefile.com b/usr/src/lib/udapl/libdat/Makefile.com index 8694c77788..438198ebde 100644 --- a/usr/src/lib/udapl/libdat/Makefile.com +++ b/usr/src/lib/udapl/libdat/Makefile.com @@ -22,6 +22,8 @@ # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2019 Joyent, Inc. +# LIBRARY= libdat.a VERS= .1 @@ -40,19 +42,19 @@ OBJECTS = \ include ../../../Makefile.lib -LIBS = $(DYNLIB) $(LINTLIB) +LIBS = $(DYNLIB) LDLIBS += -lc SRCDIR = ../common CPPFLAGS += -I../include CFLAGS += $(CCVERBOSE) -LINTFLAGS += -DDEBUG -LINTFLAGS64 += -DDEBUG -$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC) CERRWARN += -_gcc=-Wno-type-limits +# false positive +SMOFF += signed + $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG .KEEP_STATE: @@ -61,6 +63,4 @@ all: $(LIBS) debug: all -lint: lintcheck - include ../../../Makefile.targ diff --git a/usr/src/tools/smatch/Makefile b/usr/src/tools/smatch/Makefile index 1ed525bce7..145c08de28 100644 --- a/usr/src/tools/smatch/Makefile +++ b/usr/src/tools/smatch/Makefile @@ -13,14 +13,14 @@ # # The src/ sub-directory is un-modified copy of -# https://github.com/illumos/smatch/tree/0.5.1-il-6 +# https://github.com/illumos/smatch/tree/$SPARSE_VERSION # # This Makefile installs just enough for us to be able to run smatch # locally. # PROG = smatch -SPARSE_VERSION = 0.5.1-il-6 +SPARSE_VERSION = 0.6.1-rc1-il-1 include ../Makefile.tools @@ -51,78 +51,168 @@ CPPFLAGS += -I$(NATIVE_ADJUNCT)/include INS.file = $(RM) $@; $(CP) $< $(@D); $(CHMOD) $(FILEMODE) $@ INS.dir = mkdir -p $@; $(CHMOD) $(DIRMODE) $@ +# fine for us +OS=linux + +LIB_OBJS = +LIB_OBJS += allocate.o +LIB_OBJS += builtin.o +LIB_OBJS += char.o +LIB_OBJS += compat-$(OS).o +LIB_OBJS += cse.o +LIB_OBJS += dissect.o +LIB_OBJS += dominate.o +LIB_OBJS += evaluate.o +LIB_OBJS += expand.o +LIB_OBJS += expression.o +LIB_OBJS += flow.o +LIB_OBJS += flowgraph.o +LIB_OBJS += inline.o +LIB_OBJS += ir.o +LIB_OBJS += lib.o +LIB_OBJS += linearize.o +LIB_OBJS += liveness.o +LIB_OBJS += memops.o +LIB_OBJS += opcode.o +LIB_OBJS += optimize.o +LIB_OBJS += parse.o +LIB_OBJS += pre-process.o +LIB_OBJS += ptrlist.o +LIB_OBJS += ptrmap.o +LIB_OBJS += scope.o +LIB_OBJS += show-parse.o +LIB_OBJS += simplify.o +LIB_OBJS += sort.o +LIB_OBJS += ssa.o +LIB_OBJS += sset.o +LIB_OBJS += stats.o +LIB_OBJS += storage.o +LIB_OBJS += symbol.o +LIB_OBJS += target.o +LIB_OBJS += tokenize.o +LIB_OBJS += unssa.o +LIB_OBJS += utils.o +LIB_OBJS += macro_table.o +LIB_OBJS += token_store.o +LIB_OBJS += hashtable.o + +SMATCH_OBJS = +SMATCH_OBJS += avl.o +SMATCH_OBJS += smatch_about_fn_ptr_arg.o +SMATCH_OBJS += smatch_address.o +SMATCH_OBJS += smatch_annotate.o +SMATCH_OBJS += smatch_array_values.o +SMATCH_OBJS += smatch_assigned_expr.o +SMATCH_OBJS += smatch_bits.o +SMATCH_OBJS += smatch_buf_comparison.o +SMATCH_OBJS += smatch_buf_size.o +SMATCH_OBJS += smatch_capped.o +SMATCH_OBJS += smatch_common_functions.o +SMATCH_OBJS += smatch_comparison.o +SMATCH_OBJS += smatch_conditions.o +SMATCH_OBJS += smatch_constraints.o +SMATCH_OBJS += smatch_constraints_required.o +SMATCH_OBJS += smatch_container_of.o +SMATCH_OBJS += smatch_data_source.o +SMATCH_OBJS += smatch_db.o +SMATCH_OBJS += smatch_equiv.o +SMATCH_OBJS += smatch_estate.o +SMATCH_OBJS += smatch_expressions.o +SMATCH_OBJS += smatch_expression_stacks.o +SMATCH_OBJS += smatch_extra.o +SMATCH_OBJS += smatch_files.o +SMATCH_OBJS += smatch_flow.o +SMATCH_OBJS += smatch_fn_arg_link.o +SMATCH_OBJS += smatch_function_hooks.o +SMATCH_OBJS += smatch_function_info.o +SMATCH_OBJS += smatch_function_ptrs.o +SMATCH_OBJS += smatch_helper.o +SMATCH_OBJS += smatch_hooks.o +SMATCH_OBJS += smatch_ignore.o +SMATCH_OBJS += smatch_imaginary_absolute.o +SMATCH_OBJS += smatch_implied.o +SMATCH_OBJS += smatch_impossible.o +SMATCH_OBJS += smatch_integer_overflow.o +SMATCH_OBJS += smatch_kernel_user_data.o +SMATCH_OBJS += smatch_links.o +SMATCH_OBJS += smatch_math.o +SMATCH_OBJS += smatch_mem_tracker.o +SMATCH_OBJS += smatch_modification_hooks.o +SMATCH_OBJS += smatch_mtag_data.o +SMATCH_OBJS += smatch_mtag_map.o +SMATCH_OBJS += smatch_mtag.o +SMATCH_OBJS += smatch_nul_terminator.o +SMATCH_OBJS += smatch_param_cleared.o +SMATCH_OBJS += smatch_param_compare_limit.o +SMATCH_OBJS += smatch_parameter_names.o +SMATCH_OBJS += smatch_param_filter.o +SMATCH_OBJS += smatch_param_limit.o +SMATCH_OBJS += smatch_param_set.o +SMATCH_OBJS += smatch_param_to_mtag_data.o +SMATCH_OBJS += smatch_param_used.o +SMATCH_OBJS += smatch_parse_call_math.o +SMATCH_OBJS += smatch_passes_array_size.o +SMATCH_OBJS += smatch_project.o +SMATCH_OBJS += smatch_ranges.o +SMATCH_OBJS += smatch_real_absolute.o +SMATCH_OBJS += smatch_recurse.o +SMATCH_OBJS += smatch_returns.o +SMATCH_OBJS += smatch_return_to_param.o +SMATCH_OBJS += smatch_scope.o +SMATCH_OBJS += smatch_slist.o +SMATCH_OBJS += smatch_start_states.o +SMATCH_OBJS += smatch_statement_count.o +SMATCH_OBJS += smatch_states.o +SMATCH_OBJS += smatch_stored_conditions.o +SMATCH_OBJS += smatch_string_list.o +SMATCH_OBJS += smatch_strings.o +SMATCH_OBJS += smatch_strlen.o +SMATCH_OBJS += smatch_struct_assignment.o +SMATCH_OBJS += smatch_sval.o +SMATCH_OBJS += smatch_tracker.o +SMATCH_OBJS += smatch_type_links.o +SMATCH_OBJS += smatch_type.o +SMATCH_OBJS += smatch_type_val.o +SMATCH_OBJS += smatch_unknown_value.o +SMATCH_OBJS += smatch_untracked_param.o +SMATCH_OBJS += smatch_var_sym.o + SMATCH_CHECK_OBJS:sh=ls src/check_*.c | sed -e 's+\.c+.o+;s+src/++;' -OBJS = smatch.o $(SMATCH_CHECK_OBJS) - -OBJS += smatch_flow.o smatch_conditions.o smatch_slist.o smatch_states.o \ - smatch_helper.o smatch_type.o smatch_hooks.o smatch_function_hooks.o \ - smatch_modification_hooks.o smatch_extra.o smatch_estate.o smatch_math.o \ - smatch_sval.o smatch_ranges.o smatch_implied.o smatch_ignore.o smatch_project.o \ - smatch_var_sym.o smatch_tracker.o smatch_files.o smatch_expression_stacks.o \ - smatch_equiv.o smatch_buf_size.o smatch_strlen.o smatch_capped.o smatch_db.o \ - smatch_expressions.o smatch_returns.o smatch_parse_call_math.o \ - smatch_param_limit.o smatch_param_filter.o \ - smatch_param_set.o smatch_comparison.o smatch_param_compare_limit.o smatch_local_values.o \ - smatch_function_ptrs.o smatch_annotate.o smatch_string_list.o \ - smatch_param_cleared.o smatch_start_states.o \ - smatch_recurse.o smatch_data_source.o smatch_type_val.o \ - smatch_common_functions.o smatch_struct_assignment.o \ - smatch_unknown_value.o smatch_stored_conditions.o avl.o \ - smatch_function_info.o smatch_links.o smatch_auto_copy.o \ - smatch_type_links.o smatch_untracked_param.o smatch_impossible.o \ - smatch_strings.o smatch_param_used.o smatch_container_of.o smatch_address.o \ - smatch_buf_comparison.o smatch_real_absolute.o smatch_scope.o \ - smatch_imaginary_absolute.o smatch_parameter_names.o \ - smatch_return_to_param.o smatch_passes_array_size.o \ - smatch_constraints.o smatch_constraints_required.o \ - smatch_fn_arg_link.o smatch_about_fn_ptr_arg.o smatch_mtag.o \ - smatch_mtag_map.o smatch_mtag_data.o \ - smatch_param_to_mtag_data.o smatch_mem_tracker.o smatch_array_values.o \ - smatch_nul_terminator.o smatch_assigned_expr.o smatch_kernel_user_data.o \ - smatch_statement_count.o smatch_bits.o smatch_integer_overflow.o - -OBJS += target.o parse.o tokenize.o pre-process.o symbol.o lib.o scope.o \ - expression.o show-parse.o evaluate.o expand.o inline.o linearize.o \ - char.o sort.o allocate.o compat-linux.o ptrlist.o \ - builtin.o \ - stats.o \ - flow.o cse.o simplify.o memops.o liveness.o storage.o unssa.o \ - dissect.o \ - macro_table.o token_store.o hashtable.o +OBJS = smatch.o $(LIB_OBJS) $(SMATCH_OBJS) $(SMATCH_CHECK_OBJS) SMATCH_DATA = \ illumos_kernel.skipped_functions \ illumos_user.skipped_functions -SMATCH_DB_DATA = \ - return_states.schema \ - call_implies.schema \ - type_value.schema \ - param_map.schema \ - function_type_size.schema \ - parameter_name.schema \ - fn_ptr_data_link.schema \ - constraints.schema \ - mtag_about.schema \ - type_info.schema \ - function_type_info.schema \ - caller_info.schema \ - function_type_value.schema \ - return_implies.schema \ - type_size.schema \ - constraints_required.schema \ - fn_data_link.schema \ - mtag_alias.schema \ - common_caller_info.schema \ - data_info.schema \ - function_type.schema \ - db.schema \ - mtag_data.schema \ - function_ptr.schema \ - sink_info.schema \ - local_values.schema \ - mtag_map.schema +SMATCH_DB_DATA = +SMATCH_DB_DATA += call_implies.schema +SMATCH_DB_DATA += function_ptr.schema +SMATCH_DB_DATA += mtag_map.schema +SMATCH_DB_DATA += caller_info.schema +SMATCH_DB_DATA += function_type.schema +SMATCH_DB_DATA += param_map.schema +SMATCH_DB_DATA += common_caller_info.schema +SMATCH_DB_DATA += function_type_info.schema +SMATCH_DB_DATA += parameter_name.schema +SMATCH_DB_DATA += constraints.schema +SMATCH_DB_DATA += function_type_size.schema +SMATCH_DB_DATA += return_implies.schema +SMATCH_DB_DATA += constraints_required.schema +SMATCH_DB_DATA += function_type_value.schema +SMATCH_DB_DATA += return_states.schema +SMATCH_DB_DATA += data_info.schema +SMATCH_DB_DATA += local_values.schema +SMATCH_DB_DATA += sink_info.schema +SMATCH_DB_DATA += db.schema +SMATCH_DB_DATA += mtag_about.schema +SMATCH_DB_DATA += type_info.schema +SMATCH_DB_DATA += fn_data_link.schema +SMATCH_DB_DATA += mtag_alias.schema +SMATCH_DB_DATA += type_size.schema +SMATCH_DB_DATA += fn_ptr_data_link.schema +SMATCH_DB_DATA += mtag_data.schema +SMATCH_DB_DATA += type_value.schema ROOTONBLDDATAFILES = $(SMATCH_DATA:%=$(SMATCHDATADIR)/smatch_data/%) ROOTONBLDDATAFILES += $(SMATCH_DB_DATA:%=$(SMATCHDATADIR)/smatch_data/db/%) diff --git a/usr/src/tools/smatch/src/Documentation/.gitignore b/usr/src/tools/smatch/src/Documentation/.gitignore new file mode 100644 index 0000000000..6c08d03c89 --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/.gitignore @@ -0,0 +1,2 @@ +build +dev-options.1 diff --git a/usr/src/tools/smatch/src/Documentation/IR.rst b/usr/src/tools/smatch/src/Documentation/IR.rst new file mode 100644 index 0000000000..8ffc921aba --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/IR.rst @@ -0,0 +1,427 @@ +.. default-domain:: ir + +Sparse's Intermediate Representation +==================================== + +Instructions +~~~~~~~~~~~~ + +This document briefly describes which field of struct instruction is +used by which operation. + +Some of those fields are used by almost all instructions, +some others are specific to only one or a few instructions. +The common ones are: + +* .src1, .src2, .src3: (pseudo_t) operands of binops or ternary ops. +* .src: (pseudo_t) operand of unary ops (alias for .src1). +* .target: (pseudo_t) result of unary, binary & ternary ops, is + sometimes used otherwise by some others instructions. +* .cond: (pseudo_t) input operands for condition (alias .src/.src1) +* .type: (symbol*) usually the type of .result, sometimes of the operands + +Terminators +----------- +.. op:: OP_RET + Return from subroutine. + + * .src : returned value (NULL if void) + * .type: type of .src + +.. op:: OP_BR + Unconditional branch + + * .bb_true: destination basic block + +.. op:: OP_CBR + Conditional branch + + * .cond: condition + * .type: type of .cond, must be an integral type + * .bb_true, .bb_false: destination basic blocks + +.. op:: OP_SWITCH + Switch / multi-branch + + * .cond: condition + * .type: type of .cond, must be an integral type + * .multijmp_list: pairs of case-value - destination basic block + +.. op:: OP_COMPUTEDGOTO + Computed goto / branch to register + + * .src: address to branch to (void*) + * .multijmp_list: list of possible destination basic blocks + +Arithmetic binops +----------------- +They all follow the same signature: + * .src1, .src1: operands (types must be compatible with .target) + * .target: result of the operation (must be an integral type) + * .type: type of .target + +.. op:: OP_ADD + Integer addition. + +.. op:: OP_SUB + Integer subtraction. + +.. op:: OP_MUL + Integer multiplication. + +.. op:: OP_DIVU + Integer unsigned division. + +.. op:: OP_DIVS + Integer signed division. + +.. op:: OP_MODU + Integer unsigned remainder. + +.. op:: OP_MODS + Integer signed remainder. + +.. op:: OP_SHL + Shift left (integer only) + +.. op:: OP_LSR + Logical Shift right (integer only) + +.. op:: OP_ASR + Arithmetic Shift right (integer only) + +Floating-point binops +--------------------- +They all follow the same signature: + * .src1, .src1: operands (types must be compatible with .target) + * .target: result of the operation (must be a floating-point type) + * .type: type of .target + +.. op:: OP_FADD + Floating-point addition. + +.. op:: OP_FSUB + Floating-point subtraction. + +.. op:: OP_FMUL + Floating-point multiplication. + +.. op:: OP_FDIV + Floating-point division. + +Logical ops +----------- +They all follow the same signature: + * .src1, .src2: operands (types must be compatible with .target) + * .target: result of the operation + * .type: type of .target, must be an integral type + +.. op:: OP_AND + Logical AND + +.. op:: OP_OR + Logical OR + +.. op:: OP_XOR + Logical XOR + +Integer compares +---------------- +They all have the following signature: + * .src1, .src2: operands (types must be compatible) + * .target: result of the operation (0/1 valued integer) + * .type: type of .target, must be an integral type + +.. op:: OP_SET_EQ + Compare equal. + +.. op:: OP_SET_NE + Compare not-equal. + +.. op:: OP_SET_LE + Compare less-than-or-equal (signed). + +.. op:: OP_SET_GE + Compare greater-than-or-equal (signed). + +.. op:: OP_SET_LT + Compare less-than (signed). + +.. op:: OP_SET_GT + Compare greater-than (signed). + +.. op:: OP_SET_B + Compare less-than (unsigned). + +.. op:: OP_SET_A + Compare greater-than (unsigned). + +.. op:: OP_SET_BE + Compare less-than-or-equal (unsigned). + +.. op:: OP_SET_AE + Compare greater-than-or-equal (unsigned). + +Floating-point compares +----------------------- +They all have the same signature as the integer compares. + +The usual 6 operations exist in two versions: 'ordered' and +'unordered'. These operations first check if any operand is a +NaN and if it is the case the ordered compares return false +and then unordered return true, otherwise the result of the +comparison, now guaranteed to be done on non-NaNs, is returned. + +.. op:: OP_FCMP_OEQ + Floating-point compare ordered equal + +.. op:: OP_FCMP_ONE + Floating-point compare ordered not-equal + +.. op:: OP_FCMP_OLE + Floating-point compare ordered less-than-or-equal + +.. op:: OP_FCMP_OGE + Floating-point compare ordered greater-or-equal + +.. op:: OP_FCMP_OLT + Floating-point compare ordered less-than + +.. op:: OP_FCMP_OGT + Floating-point compare ordered greater-than + + +.. op:: OP_FCMP_UEQ + Floating-point compare unordered equal + +.. op:: OP_FCMP_UNE + Floating-point compare unordered not-equal + +.. op:: OP_FCMP_ULE + Floating-point compare unordered less-than-or-equal + +.. op:: OP_FCMP_UGE + Floating-point compare unordered greater-or-equal + +.. op:: OP_FCMP_ULT + Floating-point compare unordered less-than + +.. op:: OP_FCMP_UGT + Floating-point compare unordered greater-than + + +.. op:: OP_FCMP_ORD + Floating-point compare ordered: return true if both operands are ordered + (none of the operands are a NaN) and false otherwise. + +.. op:: OP_FCMP_UNO + Floating-point compare unordered: return false if no operands is ordered + and true otherwise. + +Unary ops +--------- +.. op:: OP_NOT + Logical not. + + * .src: operand (type must be compatible with .target) + * .target: result of the operation + * .type: type of .target, must be an integral type + +.. op:: OP_NEG + Integer negation. + + * .src: operand (type must be compatible with .target) + * .target: result of the operation (must be an integral type) + * .type: type of .target + +.. op:: OP_FNEG + Floating-point negation. + + * .src: operand (type must be compatible with .target) + * .target: result of the operation (must be a floating-point type) + * .type: type of .target + +.. op:: OP_SYMADDR + Create a pseudo corresponding to the address of a symbol. + + * .src: input symbol (must be a PSEUDO_SYM) + * .target: symbol's address + +.. op:: OP_COPY + Copy (only needed after out-of-SSA). + + * .src: operand (type must be compatible with .target) + * .target: result of the operation + * .type: type of .target + +Type conversions +---------------- +They all have the following signature: + * .src: source value + * .orig_type: type of .src + * .target: result value + * .type: type of .target + +Currently, a cast to a void pointer is treated like a cast to +an unsigned integer of the same size. + +.. op:: OP_TRUNC + Cast from integer to an integer of a smaller size. + +.. op:: OP_SEXT + Cast from integer to an integer of a bigger size with sign extension. + +.. op:: OP_ZEXT + Cast from integer to an integer of a bigger size with zero extension. + +.. op:: OP_UTPTR + Cast from pointer-sized unsigned integer to pointer type. + +.. op:: OP_PTRTU + Cast from pointer type to pointer-sized unsigned integer. + +.. op:: OP_PTRCAST + Cast between pointers. + +.. op:: OP_FCVTU + Conversion from float type to unsigned integer. + +.. op:: OP_FCVTS + Conversion from float type to signed integer. + +.. op:: OP_UCVTF + Conversion from unsigned integer to float type. + +.. op:: OP_SCVTF + Conversion from signed integer to float type. + +.. op:: OP_FCVTF + Conversion between float types. + +Ternary ops +----------- +.. op:: OP_SEL + * .src1: condition, must be of integral type + * .src2, .src3: operands (types must be compatible with .target) + * .target: result of the operation + * .type: type of .target + +.. op:: OP_RANGE + Range/bounds checking (only used for an unused sparse extension). + + * .src1: value to be checked + * .src2, src3: bound of the value (must be constants?) + * .type: type of .src[123]? + +Memory ops +---------- +.. op:: OP_LOAD + Load. + + * .src: base address to load from + * .offset: address offset + * .target: loaded value + * .type: type of .target + +.. op:: OP_STORE + Store. + + * .src: base address to store to + * .offset: address offset + * .target: value to be stored + * .type: type of .target + +Others +------ +.. op:: OP_SETFVAL + Create a pseudo corresponding to a floating-point literal. + + * .fvalue: the literal's value (long double) + * .target: the corresponding pseudo + * .type: type of the literal & .target + +.. op:: OP_SETVAL + Create a pseudo corresponding to a string literal or a label-as-value. + The value is given as an expression EXPR_STRING or EXPR_LABEL. + + * .val: (expression) input expression + * .target: the resulting value + * .type: type of .target, the value + +.. op:: OP_PHI + Phi-node (for SSA form). + + * .phi_list: phi-operands (type must be compatible with .target) + * .target: "result" + * .type: type of .target + +.. op:: OP_PHISOURCE + Phi-node source. + Like OP_COPY but exclusively used to give a defining instructions + (and thus also a type) to *all* OP_PHI operands. + + * .phi_src: operand (type must be compatible with .target, alias .src) + * .target: the "result" PSEUDO_PHI + * .type: type of .target + * .phi_users: list of phi instructions using the target pseudo + +.. op:: OP_CALL + Function call. + + * .func: (pseudo_t) the function (can be a symbol or a "register", + alias .src)) + * .arguments: (pseudo_list) list of the associated arguments + * .target: function return value (if any) + * .type: type of .target + * .fntypes: (symbol_list) list of the function's types: the first + entry is the full function type, the next ones are the type of + each arguments + +.. op:: OP_INLINED_CALL + Only used as an annotation to show that the instructions just above + correspond to a function that have been inlined. + + * .func: (pseudo_t) the function (must be a symbol, alias .src)) + * .arguments: list of pseudos that where the function's arguments + * .target: function return value (if any) + * .type: type of .target + +.. op:: OP_SLICE + Extract a "slice" from an aggregate. + + * .base: (pseudo_t) aggregate (alias .src) + * .from, .len: offet & size of the "slice" within the aggregate + * .target: result + * .type: type of .target + +.. op:: OP_ASM + Inlined assembly code. + + * .string: asm template + * .asm_rules: asm constraints, rules + +Sparse tagging (line numbers, context, whatever) +------------------------------------------------ +.. op:: OP_CONTEXT + Currently only used for lock/unlock tracking. + + * .context_expr: unused + * .increment: (1 for locking, -1 for unlocking) + * .check: (ignore the instruction if 0) + +Misc ops +-------- +.. op:: OP_ENTRY + Function entry point (no associated semantic). + +.. op:: OP_BADOP + Invalid operation (should never be generated). + +.. op:: OP_NOP + No-op (should never be generated). + +.. op:: OP_DEATHNOTE + Annotation telling the pseudo will be death after the next + instruction (other than some other annotation, that is). + +.. # vim: tabstop=4 diff --git a/usr/src/tools/smatch/src/Documentation/Makefile b/usr/src/tools/smatch/src/Documentation/Makefile new file mode 100644 index 0000000000..2e8cbdfd76 --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/Makefile @@ -0,0 +1,26 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = -a +SPHINXBUILD = sphinx-build +SPHINXPROJ = sparse +SOURCEDIR = . +BUILDDIR = build + +targets := help +targets += html +targets += man + + +# Put it first so that "make" without argument is like "make help". +help: + +# route all targets to Sphinx using the new "make mode" option. +$(targets): conf.py Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) + +%.1: %.rst man + @mv build/man/$@ $@ + +.PHONY: Makefile # avoid circular deps with the catch-all rule diff --git a/usr/src/tools/smatch/src/Documentation/TODO.md b/usr/src/tools/smatch/src/Documentation/TODO.md new file mode 100644 index 0000000000..cbda1c397e --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/TODO.md @@ -0,0 +1,98 @@ +TODO +==== + +Essential +--------- +* SSA is broken by simplify_loads() & branches rewriting/simplification +* attributes of struct, union & enums are ignored (and possibly in other + cases too). +* add support for bitwise enums + +Documentation +------------- +* document the extensions +* document the API +* document the limitations of modifying ptrlists during list walking +* document the data structures +* document flow of data / architecture / code structure + +Core +---- +* if a variable has its address taken but in an unreachable BB then + its MOD_ADDRESSABLE may be wrong and it won't be SSA converted. + - let kill_insn() check killing of SYMADDR, + - add the sym into a list and + - recalculate the addressability before memops's SSA conversion +* bool_ctype should be split into internal 1-bit / external 8-bit +* Previous declarations and the definition need to be merged. For example, + in the code here below, the function definition is **not** static: + ``` + static void foo(void); + void foo(void) { ... } + ``` + +Testsuite +-------- +* there are more than 50 failing tests. They should be fixed + (but most are non-trivial to fix). + +Misc +---- +* GCC's -Wenum-compare / clangs's -Wenum-conversion -Wassign-enum +* parse __attribute_((fallthrough)) +* add support for __builtin_unreachable() +* add support for format(printf()) (WIP by Ben Dooks) +* make use of UNDEFs (issues warnings, simplification, ... ?) +* add a pass to inline small functions during simplification. + +Optimization +------------ +* the current way of doing CSE uses a lot of time +* add SSA based DCE +* add SSA based PRE +* Add SSA based SCCP +* use better/more systematic use of internal verification framework + +IR +-- +* OP_SET should return a bool, always +* add IR instructions for va_arg() & friends +* add a possibility to import of file in "IR assembly" +* dump the symtable +* dump the CFG + +LLVM +---- +* fix ... + +Internal backends +----------------- +* add some basic register allocation +* add a pass to transform 3-addresses code to 2-addresses +* what can be done for x86? + +Longer term/to investigate +-------------------------- +* better architecture handling than current machine.h + target.c +* attributes are represented as ctypes's alignment, modifiers & contexts + but plenty of attributes doesn't fit, for example they need arguments. + * format(printf, ...), + * section("...") + * assume_aligned(alignment[, offsert]) + * error("message"), warning("message") + * ... +* should support "-Werror=..." ? +* All warning messages should include the option how to disable it. + For example: + "warning: Variable length array is used." + should be something like: + "warning: Variable length array is used. (-Wno-vla)" +* ptrlists must have elements be removed while being iterated but this + is hard to insure it is not done. +* having 'struct symbol' used to represent symbols *and* types is + quite handy but it also creates lots of problems and complications +* Possible mixup of symbol for a function designator being not a pointer? + This seems to make evaluation of function pointers much more complex + than needed. +* extend test-inspect to inspect more AST fields. +* extend test-inspect to inspect instructions. diff --git a/usr/src/tools/smatch/src/Documentation/api.rst b/usr/src/tools/smatch/src/Documentation/api.rst new file mode 100644 index 0000000000..cb8a098201 --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/api.rst @@ -0,0 +1,27 @@ +Sparse API +========== + +.. contents:: + :local: + :depth: 2 + +Utilities +~~~~~~~~~ + +.. c:autodoc:: ptrlist.c +.. c:autodoc:: utils.h + +Parsing +~~~~~~~ + +.. c:autodoc:: expression.h + +Typing +~~~~~~ + +.. c:autodoc:: evaluate.h + +Optimization +~~~~~~~~~~~~ + +.. c:autodoc:: simplify.c diff --git a/usr/src/tools/smatch/src/Documentation/arm64-detecting-tagged-addresses.txt b/usr/src/tools/smatch/src/Documentation/arm64-detecting-tagged-addresses.txt new file mode 100644 index 0000000000..0f421c825a --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/arm64-detecting-tagged-addresses.txt @@ -0,0 +1,207 @@ +Detecting ARM64 tagged pointers +=============================== + +The ARM64 ABI allows tagged memory addresses to be passed through the +user-kernel syscall ABI boundary. Tagged memory addresses are those which +contain a non-zero top byte - the hardware will always ignore this top +byte, however software does not. Therefore it is helpful to be able to +detect code that erroneously compares tagged memory addresses with +untagged memory addresses. This document describes how smatch can be used +for this. + +Smatch will provide a warning when it detects that a comparison is being +made between a user originated 64 bit data where the top byte may be +non-zero and any variable which may contain an untagged address. + +Untagged variables are detected by looking for hard-coded known struct +members (such as vm_start, vm_end and addr_limit) and hard-coded known +macros (such as PAGE_SIZE, PAGE_MASK and TASK_SIZE). This check is +also able to detect when comparisons are made against variables that +have been assigned from these known untagged variables, though this +tracking is limited to the scope of the function. + +This check is only performed when the ARCH environment variable is set to +arm64. To provide a worked example, consider the following command which is +used to perform Smatch static analysis on the Linux kernel: + +$ ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- ~/smatch/smatch_scripts/build_kernel_data.sh + +It is recommended that this command is run multiple times (6 or more) to +provide Smatch with a deeper knowledge of the call stack. Before running +multiple iterations of Smatch, it may be beneficial to delete any smatch* +files in the root of the linux tree. + +Once Smatch has run, you can observe warnings as follows: + +$ cat smatch_warns.txt | grep "tagged address" +mm/gup.c:818 __get_user_pages() warn: comparison of a potentially tagged +address (__get_user_pages, 2, start) +... + +This warning tells us that on line 818 of mm/gup.c an erroneous comparison +may have been made between a tagged address (variable 'start' which originated +from parameter 2 of the function) and existing kernel addresses (untagged). + +The code that this relates to follows: + +790: static long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm, +791: unsigned long start, unsigned long nr_pages, +792: unsigned int gup_flags, struct page **pages, +793: struct vm_area_struct **vmas, int *nonblocking) +794:{ +... +818: if (!vma || start >= vma->vm_end) { + +Through manual inspection of this code, we can verify that the variable 'start' +originated from parameter 2 of its function '__get_user_pages'. + +A suggested fix at this point may be to call the untagged_addr macro prior +to the comparison on line 818. However it's often helpful to follow the +parameter up the call stack, we can do this with the following Smatch command: + +$ ~/smatch/smatch_data/db/smdb.py find_tagged __get_user_pages 2 + copy_strings (param ?) -> get_arg_page (param 1) + vfio_pin_map_dma (param ?) -> vfio_pin_pages_remote (param 1) + __se_sys_ptrace (param 2) + environ_read (param ?) -> access_remote_vm (param 1) + io_sqe_buffer_register (param ?) -> get_user_pages (param 0) + gntdev_grant_copy_seg (param ?) -> gntdev_get_page (param 1) + get_futex_key (param ?) -> get_user_pages_fast (param 0) + __se_sys_madvise (param 0) + __mm_populate (param ?) -> populate_vma_page_range (param 1) + __se_sys_mprotect (param 0) + + +This script will examine all of the possible callers of __get_user_pages where +parameter 2 contains user data and where the top byte of the parameter may be +non-zero. It will recurse up the possible call stacks as far as it can go. This +will leave a list of functions that provide tagged addresses to __get_user_pages +and the parameter of interest (or variable if Smatch cannot determine the +function parameter). + +Sometimes Smatch is able to determine a caller of a function but is unable +to determine which parameter of that function relates to the parameter of the +called function, when this happens the following output it shown: + +get_futex_key (param ?) -> get_user_pages_fast (param 0) + +This shows that when following up the call tree from __get_user_pages, we stop +at get_user_pages_fast with parameter 0 of that function containing user data. +Smatch knows that get_futex_key calls get_user_pages_fast but cannot determine +which parameter of get_futex_key provided the data of interest. In these cases +manual inspection of the source tree can help and if necessary re-run the +smdb.py script with new parameters (e.g. smdb.py find_tagged get_futex_key 0). + +To provide a summary of all of the tagged issues found, the following command +can be run directly on the smatch_warns.txt file: + +$ ~/smatch/smatch_data/db/smdb.py parse_warns_tagged smatch_warns.txt + +This will run find_tagged for each issue found, e.g. + +mm/mmap.c:2918 (func: __do_sys_remap_file_pages, param: 0:start) may be caused by: + __se_sys_remap_file_pages (param 0) + +mm/mmap.c:2963 (func: __do_sys_remap_file_pages, param: -1:__UNIQUE_ID___y73) may be caused by: + __do_sys_remap_file_pages (variable __UNIQUE_ID___y73 (can't walk call tree) + +mm/mmap.c:3000 (func: do_brk_flags, param: -1:error) may be caused by: + do_brk_flags (variable error (can't walk call tree) + +mm/mmap.c:540 (func: find_vma_links, param: 1:addr) may be caused by: + find_vma_links (param 1) (can't walk call tree) + +mm/mmap.c:570 (func: count_vma_pages_range, param: -1:__UNIQUE_ID___x64) may be caused by: + count_vma_pages_range (variable __UNIQUE_ID___x64 (can't walk call tree) + +mm/mmap.c:580 (func: count_vma_pages_range, param: -1:__UNIQUE_ID___x68) may be caused by: + count_vma_pages_range (variable __UNIQUE_ID___x68 (can't walk call tree) + +mm/mmap.c:856 (func: __vma_adjust, param: 1:start) may be caused by: + __se_sys_mprotect (param 0) + __se_sys_mlock (param 0) + __se_sys_mlock2 (param 0) + __se_sys_munlock (param 0) + mbind_range (param ?) -> vma_merge (param 2) + __se_sys_madvise (param 0) + __se_sys_mbind (param 0) + + +The above commands do not output a call stack, instead they provide the 'highest' +caller found, to provide a call stack perform the following: + +$ ~/smatch/smatch_data/db/smdb.py call_tree __get_user_pages +__get_user_pages() + __get_user_pages_locked() + get_user_pages_remote() + get_arg_page() + copy_strings() + remove_arg_zero() + vaddr_get_pfn() + vfio_pin_pages_remote() + vfio_pin_page_external() + process_vm_rw_single_vec() + process_vm_rw_core() + __access_remote_vm() + ptrace_access_vm() + access_remote_vm() + access_process_vm() + check_and_migrate_cma_pages() + __gup_longterm_locked() + get_user_pages() + __gup_longterm_unlocked() + get_user_pages_locked() + get_vaddr_frames() + vb2_create_framevec() + lookup_node() + do_get_mempolicy() + get_user_pages_unlocked() + hva_to_pfn_slow() + hva_to_pfn() + +Please note that this will show all the callers and is not filtered for those +carrying tagged addresses in their parameters. + +It is possible to filter out false positives by annotating function parameters +with __untagged. For example: + +unsigned long do_mmap(struct file *file, unsigned long addr, + unsigned long __untagged len, unsigned long prot, + unsigned long flags, vm_flags_t vm_flags, + unsigned long pgoff, unsigned long *populate, + struct list_head *uf) +{ + +This annotation tells smatch that regardless to the value stored in 'len' it +should be treated as an untagged address. As Smatch is able to track the +potential ranges of values a variable may hold, it will also track the +annotation - therefore it is not necessary to use the annotation in every +function that do_mmap calls. When using this annotation smdb.py will filter +out functions that carry a value which has been annotated as untagged. Please +note that due to limitations in parameter tracking some annotations will be +ignored and not propogated all the way down the call tree. + +Finally, the following patch is required to add annotations to the Linux +kernel: + +diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h +index 19e58b9138a0..755e8df375a5 100644 +--- a/include/linux/compiler_types.h ++++ b/include/linux/compiler_types.h +@@ -19,6 +19,7 @@ + # define __cond_lock(x,c) ((c) ? ({ __acquire(x); 1; }) : 0) + # define __percpu __attribute__((noderef, address_space(3))) + # define __rcu __attribute__((noderef, address_space(4))) ++# define __untagged __attribute__((address_space(5))) + # define __private __attribute__((noderef)) + extern void __chk_user_ptr(const volatile void __user *); + extern void __chk_io_ptr(const volatile void __iomem *); +@@ -45,6 +46,7 @@ extern void __chk_io_ptr(const volatile void __iomem *); + # define __cond_lock(x,c) (c) + # define __percpu + # define __rcu ++# define __untagged + # define __private + # define ACCESS_PRIVATE(p, member) ((p)->member) + #endif /* __CHECKER__ */ + diff --git a/usr/src/tools/smatch/src/Documentation/conf.py b/usr/src/tools/smatch/src/Documentation/conf.py new file mode 100644 index 0000000000..aae9d39bb2 --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/conf.py @@ -0,0 +1,178 @@ +# -*- coding: utf-8 -*- +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another +# directory, add these directories to sys.path here. If the directory +# is relative to the documentation root, use os.path.abspath to make +# it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('.')) + +import os +import sys +import datetime + +# -- General configuration ------------------------------------------------ + +needs_sphinx = '1.3' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +sys.path.insert(0, os.path.abspath('sphinx')) +extensions = [ + 'cdoc' + , 'ir' +] + +# support .md with python2 & python3 +if sys.version_info[0] > 2: + from recommonmark.parser import CommonMarkParser + source_parsers = { + '.md': CommonMarkParser, + } +else: + source_parsers = { + '.md': 'recommonmark.parser.CommonMarkParser', + } + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +source_suffix = ['.rst', '.md'] + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'sparse' +copyright = '2003 - ' + str(datetime.datetime.now().year) +author = "sparse's development community" + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The full version, including alpha/beta/rc tags. +release = next(open('../Makefile', 'r')).split('=')[1].rstrip() +# The short X.Y version. +version = release.split('-')[0] + +# it's a C project, so: +primary_domain = 'c' +# disable syntax highlight in non-code sections +highlight_language = 'none' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['build'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + +# -- Options for cdoc extension ------------------------------------------- + +cdoc_srcdir = '..' + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'classic' +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ['sphinx/static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# This is required for the alabaster theme +# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars +html_sidebars = { + '**': [ + 'relations.html', # needs 'show_related': True theme option to display + 'searchbox.html', + ] +} + +html_logo = 'logo.svg' + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'sparsedoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + 'papersize': 'a4paper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'sparse.tex', u'sparse Documentation', author, 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('dev-options', 'dev-options', u'options for development', [author], 1), +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'sparse', u'sparse Documentation', author, 'sparse', 'C semantic parser & checker', 'Software development'), +] + + +# vim: tabstop=4 diff --git a/usr/src/tools/smatch/src/Documentation/dev-options.rst b/usr/src/tools/smatch/src/Documentation/dev-options.rst new file mode 100644 index 0000000000..23e8096c2c --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/dev-options.rst @@ -0,0 +1,58 @@ +sparse - extra options for developers +===================================== + +SYNOPSIS +-------- +``tools`` [`options`]... `file.c`` + +DESCRIPTION +----------- + +This file is a complement of sparse's man page meant to +document options only useful for development on sparse itself. + +OPTIONS +------- + +.. option:: -fdump-ir=pass,[pass] + + Dump the IR at each of the given passes. + + The passes currently understood are: + + * ``linearize`` + * ``mem2reg`` + * ``final`` + + The default pass is ``linearize``. + +.. option:: -f<name-of-the-pass>[-disable|-enable|=last] + + If ``=last`` is used, all passes after the specified one are disabled. + By default all passes are enabled. + + The passes currently understood are: + + * ``linearize`` (can't be disabled) + * ``mem2reg`` + * ``optim`` + +.. option:: -vcompound + + Print all compound global data symbols with their sizes and alignment. + +.. option:: -vdead + + Add ``OP_DEATHNOTE`` annotations to dead pseudos. + +.. option:: -vdomtree + + Dump the dominance tree after its calculation. + +.. option:: -ventry + + Dump the IR after all optimization passes. + +.. option:: -vpostorder + + Dump the reverse postorder traversal of the CFG. diff --git a/usr/src/tools/smatch/src/Documentation/doc-guide.rst b/usr/src/tools/smatch/src/Documentation/doc-guide.rst new file mode 100644 index 0000000000..8133cb3a59 --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/doc-guide.rst @@ -0,0 +1,155 @@ +How to write sparse documentation +================================= + +Introduction +------------ + + +The documentation for Sparse is written in plain text augmented with +either `reStructuredText`_ (.rst) or `MarkDown`_ (.md) markup. These +files can be organized hierarchically, allowing a good structuring +of the documentation. +Sparse uses `Sphinx`_ to format this documentation in several formats, +like HTML or PDF. + +All documentation related files are in the ``Documentation/`` directory. +In this directory you can use ``make html`` or ``make man`` to generate +the documentation in HTML or manpage format. The generated files can then +be found in the ``build/`` sub-directory. + +The root of the documentation is the file ``index.rst`` which mainly +lists the names of the files with real documentation. + +.. _Sphinx: http://www.sphinx-doc.org/ +.. _reStructuredText: http://docutils.sourceforge.net/rst.html +.. _MarkDown: https://en.wikipedia.org/wiki/Markdown + + +.. _rest-markup: + +Minimal reST cheatsheet +----------------------- + +Basic inline markup is: + +* ``*italic*`` gives *italic* +* ``**bold**`` gives **bold** +* ````mono```` gives ``mono`` + +Headings are created by underlining the title with a punctuation +character; it can also be optionally overlined:: + + ############# + Major heading + ############# + + Minor heading + ------------- + +Any punctuation character can be used and the levels are automatically +determined from their nesting. However, the convention is to use: + +* ``#`` with overline for parts +* ``*`` with overline for chapters +* ``=`` for sections +* ``-`` for subsections +* ``^`` for subsubsections + + +Lists can be created like this:: + + * this is a bulleted list + * with the second item + on two lines + * nested lists are supported + + * subitem + * another subitem + + * and here is the fourth item + + #. this is an auto-numbered list + #. with two items + + 1. this is an explicitly numbered list + 2. with two items + + +Definition lists are created with a simple indentation, like:: + + term, concept, whatever + Definition, must be indented and + continue here. + + It can also have several paragraphs. + +Literal blocks are introduced with ``::``, either at the end of the +preceding paragraph or on its own line, and indented text:: + + This is a paragraph introducing a literal block:: + + This is the literal block. + It can span several lines. + + It can also consist of several paragraphs. + +Code examples with syntax highlighting use the *code-block* directive. +For example:: + + .. code-block:: c + + int foo(int a) + { + return a + 1; + } + +will give: + +.. code-block:: c + + int foo(int a) + { + return a + 1; + } + + +Autodoc +------- + +.. highlight:: none +.. c:autodoc:: Documentation/sphinx/cdoc.py + +For example, a doc-block like:: + + /// + // increment a value + // + // @val: the value to increment + // @return: the incremented value + // + // This function is to be used to increment a + // value. + // + // It's strongly encouraged to use this + // function instead of open coding a simple + // ``++``. + int inc(int val) + +will be displayed like this: + +.. c:function:: int inc(int val) + :noindex: + + :param val: the value to increment + :return: the incremented value + + This function is to be used to increment a + value. + + It's strongly encouraged to use this + function instead of open coding a simple + ``++``. + +Intermediate Representation +--------------------------- +.. c:autodoc:: Documentation/sphinx/ir.py diff --git a/usr/src/tools/smatch/src/Documentation/index.rst b/usr/src/tools/smatch/src/Documentation/index.rst new file mode 100644 index 0000000000..f8ca0dcee2 --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/index.rst @@ -0,0 +1,38 @@ +.. sparse documentation master file. + +Welcome to sparse's documentation +================================= + +.. toctree:: + :maxdepth: 1 + +User documentation +------------------ +.. toctree:: + :maxdepth: 1 + + nocast-vs-bitwise + +Developer documentation +----------------------- +.. toctree:: + :maxdepth: 1 + + test-suite + dev-options + api + IR + doc-guide + +How to contribute +----------------- +.. toctree:: + :maxdepth: 1 + + submitting-patches + TODO + +Indices and tables +================== + +* :ref:`genindex` diff --git a/usr/src/tools/smatch/src/Documentation/logo.svg b/usr/src/tools/smatch/src/Documentation/logo.svg new file mode 100644 index 0000000000..d91da9db11 --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/logo.svg @@ -0,0 +1,94 @@ +<?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="425.19684" + height="170.07874" + id="svg2" + version="1.1" + inkscape:version="0.48.5 r10040" + sodipodi:docname="logo.svg"> + <metadata + id="metadata24"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs22" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1122" + inkscape:window-height="764" + id="namedview20" + showgrid="false" + units="mm" + inkscape:zoom="0.41627715" + inkscape:cx="115.87347" + inkscape:cy="283.465" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:window-maximized="0" + inkscape:current-layer="svg2" /> + <g + id="g4" + transform="matrix(0.8,0,0,0.8,-15.072782,-220.54503)"> + <title + id="title6">Layer 1</title> + <path + id="svg_2" + d="m 118.652,337.78799 0,8.36701 c -8.532,-5.01502 -15.868,-7.52301 -22.008003,-7.52301 -5.905998,0 -10.839996,1.793 -14.800995,5.379 -3.962006,3.586 -5.941002,8.02801 -5.941002,13.324 0,3.797 1.170998,7.22 3.515999,10.26599 2.343002,3.04801 7.242004,5.97702 14.695,8.789 7.453001,2.81198 12.890001,5.15702 16.312001,7.03101 3.421,1.87601 6.328,4.595 8.719,8.156 2.391,3.56299 3.586,8.181 3.586,13.85199 0,7.54801 -2.767,13.922 -8.297,19.125 -5.532,5.20301 -12.282,7.805 -20.250002,7.805 -8.155998,0 -16.547997,-2.858 -25.171997,-8.57801 l 0,-8.92999 C 78.526,421.836 86.776,425.328 93.761001,425.328 c 5.811996,0 10.722999,-1.88602 14.729999,-5.66 4.00699,-3.77399 6.012,-8.40302 6.012,-13.88901 0,-4.173 -1.243,-7.935 -3.727,-11.28601 -2.484,-3.353 -7.521,-6.48499 -15.111999,-9.4 -7.590004,-2.914 -13.014999,-5.26199 -16.275002,-7.04098 -3.261002,-1.77902 -6.028,-4.32202 -8.303001,-7.625 -2.275002,-3.302 -3.411995,-7.55402 -3.411995,-12.755 0,-7.21402 2.764999,-13.362 8.296997,-18.44599 5.529999,-5.082 12.186996,-7.62402 19.969002,-7.62402 7.640998,-9.8e-4 15.210998,2.06201 22.711998,6.186 z" + inkscape:connector-curvature="0" /> + <path + id="svg_3" + d="m 139.886,332.866 27.06999,0 c 10.35901,0 18.29201,2.32 23.80101,6.961 5.50701,4.64099 8.262,10.96899 8.262,18.98401 0,8.10998 -2.777,14.53198 -8.332,19.26599 -5.555,4.73501 -13.7,7.10199 -24.43399,7.10199 l -18.77301,0 0,45.914 -7.594,0 0,-98.22699 z m 7.594,6.328 0,39.65601 18.281,0 c 8.202,0 14.437,-1.73301 18.70299,-5.20301 4.265,-3.46802 6.39799,-8.367 6.39799,-14.695 0,-6.047 -2.08599,-10.85099 -6.25799,-14.414 -4.173,-3.56199 -10.149,-5.344 -17.92999,-5.344 l -19.194,0 z" + inkscape:connector-curvature="0" /> + <path + id="svg_4" + d="m 239.248,332.16299 45.41301,98.93 -7.96802,0 -39.59999,-86.555 -39.45801,86.555 -7.96699,0 45.408,-98.93 4.172,0 z" + inkscape:connector-curvature="0" + style="fill:#ff0000" /> + <path + id="svg_5" + d="m 296.543,332.866 25.172,0 c 10.54699,0 18.60699,2.285 24.18801,6.85501 5.577,4.56997 8.36698,10.74698 8.36698,18.52698 0,11.345 -6.23499,19.31301 -18.703,23.906 3.234,1.547 7.59402,6.539 13.078,14.97702 l 22.21902,33.961 -8.99002,0 -17.10898,-26.92102 c -5.71701,-9.004 -10.169,-14.61798 -13.35501,-16.84699 -3.18701,-2.22799 -7.85,-3.34201 -13.987,-3.34201 l -13.28399,0 0,47.10901 -7.594,0 0,-98.225 -0.002,0 z m 7.59399,6.328 0,38.461 16.31201,0 c 8.297,0 14.63498,-1.67499 19.01999,-5.02701 4.38199,-3.35098 6.57401,-8.09698 6.57401,-14.23798 0,-6.04702 -2.228,-10.75802 -6.68002,-14.13302 -4.45499,-3.375 -10.85398,-5.06198 -19.19498,-5.06198 l -16.03101,0 0,-0.001 z" + inkscape:connector-curvature="0" /> + <path + id="svg_6" + d="m 425.285,337.78799 0,8.36701 c -8.53299,-5.01502 -15.86801,-7.52301 -22.00799,-7.52301 -5.90601,0 -10.841,1.793 -14.801,5.379 -3.96301,3.586 -5.94101,8.02801 -5.94101,13.324 0,3.797 1.16999,7.22 3.51599,10.26599 2.34302,3.04801 7.24201,5.97702 14.69501,8.789 7.453,2.81198 12.88998,5.15702 16.31201,7.03101 3.41999,1.87601 6.32797,4.595 8.719,8.156 2.39099,3.56299 3.586,8.181 3.586,13.85199 0,7.54801 -2.76801,13.922 -8.297,19.125 -5.53201,5.20301 -12.28201,7.805 -20.25,7.805 -8.15601,0 -16.54901,-2.858 -25.172,-8.57801 l 0,-8.92999 c 9.51499,6.98502 17.76398,10.47702 24.75,10.47702 5.81098,0 10.72299,-1.88602 14.72998,-5.66 4.00699,-3.77399 6.012,-8.40302 6.012,-13.88901 0,-4.173 -1.24299,-7.935 -3.72699,-11.28601 -2.48401,-3.353 -7.521,-6.48499 -15.112,-9.4 -7.59101,-2.914 -13.01499,-5.26199 -16.27399,-7.04098 -3.26203,-1.77902 -6.02902,-4.32202 -8.30301,-7.625 -2.27499,-3.302 -3.41199,-7.55402 -3.41199,-12.755 0,-7.21402 2.76499,-13.362 8.297,-18.44599 5.52899,-5.082 12.18698,-7.62402 19.96899,-7.62402 7.63901,-9.8e-4 15.20999,2.06201 22.711,6.186 z" + inkscape:connector-curvature="0" /> + <path + id="svg_7" + d="m 500.379,332.866 0,6.328 -45.98401,0 0,39.30499 20.60501,0 0,6.39801 -20.60501,0 0,39.86701 47.10901,0 0,6.328 -54.70301,0 0,-98.22702 53.57801,0 0,0.001 z" + inkscape:connector-curvature="0" + style="fill:#00f200" /> + <g + id="svg_8" /> + <g + id="svg_9" /> + <g + id="svg_10" /> + <g + id="svg_11" /> + <g + id="svg_12" /> + <g + id="svg_13" /> + </g> +</svg> diff --git a/usr/src/tools/smatch/src/Documentation/nocast-vs-bitwise.md b/usr/src/tools/smatch/src/Documentation/nocast-vs-bitwise.md new file mode 100644 index 0000000000..b649abcd59 --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/nocast-vs-bitwise.md @@ -0,0 +1,41 @@ +# __nocast vs __bitwise + +`__nocast` warns about explicit or implicit casting to different types. +HOWEVER, it doesn't consider two 32-bit integers to be different +types, so a `__nocast int` type may be returned as a regular `int` +type and then the `__nocast` is lost. + +So `__nocast` on integer types is usually not that powerful. It just +gets lost too easily. It's more useful for things like pointers. It +also doesn't warn about the mixing: you can add integers to `__nocast` +integer types, and it's not really considered anything wrong. + +`__bitwise` ends up being a *stronger integer separation*. That one +doesn't allow you to mix with non-bitwise integers, so now it's much +harder to lose the type by mistake. + +So the basic rule is: + + - `__nocast` on its own tends to be more useful for *big* integers +that still need to act like integers, but you want to make it much +less likely that they get truncated by mistake. So a 64-bit integer +that you don't want to mistakenly/silently be returned as `int`, for +example. But they mix well with random integer types, so you can add +to them etc without using anything special. However, that mixing also +means that the `__nocast` really gets lost fairly easily. + + - `__bitwise` is for *unique types* that cannot be mixed with other +types, and that you'd never want to just use as a random integer (the +integer `0` is special, though, and gets silently accepted - it's +kind of like `NULL` for pointers). So `gfp_t` or the `safe endianness` +types would be `__bitwise`: you can only operate on them by doing +specific operations that know about *that* particular type. + +Generally, you want `__bitwise` if you are looking for type safety. +`__nocast` really is pretty weak. + +## Reference: + +* Linus' e-mail about `__nocast` vs `__bitwise`: + + <https://marc.info/?l=linux-mm&m=133245421127324&w=2> diff --git a/usr/src/tools/smatch/src/Documentation/project-ideas.md b/usr/src/tools/smatch/src/Documentation/project-ideas.md deleted file mode 100644 index 380f850b77..0000000000 --- a/usr/src/tools/smatch/src/Documentation/project-ideas.md +++ /dev/null @@ -1,52 +0,0 @@ -Why hacking on sparse -===================== - -1. sparse is small. - The full project compiles in less than 10 seconds on old and not performing laptop. -2. sparse is fast. - Typically, sparse can check a C file 1/10 of time it takes for gcc to generate object files. -3. sparse can digest the full kernel source files. - With sparse-llvm, sparse uses llvm as back end to emit real machine code. - -New developer hacking on sparse -============================== - - -* All sparse warning messages should include the option how - to disable it. - e.g. "pre-process.c:20*:28: warning: Variable length array is used." - should be something like - "pre-process.c:20*:28: warning: Variable length array is -used. (-Wno-vla)" -* extend test-inspect to inspect more AST fields. -* extend test-inspect to inspect instructions. -* adding architecture handling in sparse similar to cgcc -* parallel processing of test-suite -* Howto: fix the kernel rcu related checker warnings -* option to disable AST level inline. -* debug: debug version of sparse do all the verification double check -* test suite: verify and compare IR (suggested by Dibyendu Majumdar) -* checker error output database - -For experienced developers -========================== - -* merge C type on incremental declare of C type and function prototype. -* move attribute out of ctype to allow easier to add new attribute. -* serialize, general object walking driven by data structures. -* serialize, write sparse byte code into file -* serialize, load sparse byte code from file. -* symbol index/linker, know which symbol in which byte code file. -* inline function in instruction level -* cross function checking -* debug: optimization step by step log -* debug: fancy animation of CFG -* phi node location (Luc has patch) -* revisit crazy programmer warning, invalid SSA form. -* ptrlist, looping while modify inside the loop. -* dead code elimination using ssa -* constant propagation using ssa. -* x86/arm back end instruction set define -* register allocation. -* emit x86/arm machine level code - diff --git a/usr/src/tools/smatch/src/Documentation/smatch.txt b/usr/src/tools/smatch/src/Documentation/smatch.txt index ae9a454dd0..b62e4507ee 100644 --- a/usr/src/tools/smatch/src/Documentation/smatch.txt +++ b/usr/src/tools/smatch/src/Documentation/smatch.txt @@ -55,6 +55,10 @@ You can also build a directory like this: The kchecker script prints its warnings to stdout. +The above scripts will ensure that any ARCH or CROSS_COMPILE environment +variables are passed to kernel build system - thus allowing for the use of +Smatch with kernels that are normally built with cross-compilers. + If you are building something else (which is not the Linux kernel) then use something like: diff --git a/usr/src/tools/smatch/src/Documentation/sparse.txt b/usr/src/tools/smatch/src/Documentation/sparse.txt deleted file mode 100644 index 383376c04f..0000000000 --- a/usr/src/tools/smatch/src/Documentation/sparse.txt +++ /dev/null @@ -1,45 +0,0 @@ -Sparse -~~~~~~ - -__nocast vs __bitwise: - -__nocast warns about explicit or implicit casting to different types. - -HOWEVER, it doesn't consider two 32-bit integers to be different -types, so a __nocast 'int' type may be returned as a regular 'int' -type and then the __nocast is lost. - -So "__nocast" on integer types is usually not that powerful. It just -gets lost too easily. It's more useful for things like pointers. It -also doesn't warn about the mixing: you can add integers to __nocast -integer types, and it's not really considered anything wrong. - -__bitwise ends up being a "stronger integer separation". That one -doesn't allow you to mix with non-bitwise integers, so now it's much -harder to lose the type by mistake. - -So the basic rule is: - - - "__nocast" on its own tends to be more useful for *big* integers -that still need to act like integers, but you want to make it much -less likely that they get truncated by mistake. So a 64-bit integer -that you don't want to mistakenly/silently be returned as "int", for -example. But they mix well with random integer types, so you can add -to them etc without using anything special. However, that mixing also -means that the __nocast really gets lost fairly easily. - - - "__bitwise" is for *unique types* that cannot be mixed with other -types, and that you'd never want to just use as a random integer (the -integer 0 is special, though, and gets silently accepted iirc - it's -kind of like "NULL" for pointers). So "gfp_t" or the "safe endianness" -types would be __bitwise: you can only operate on them by doing -specific operations that know about *that* particular type. - -Generally, you want __bitwise if you are looking for type safety. -"__nocast" really is pretty weak. - -Reference: - -* Linus' e-mail about __nocast vs __bitwise: - - http://marc.info/?l=linux-mm&m=133245421127324&w=2 diff --git a/usr/src/tools/smatch/src/Documentation/sphinx/cdoc.py b/usr/src/tools/smatch/src/Documentation/sphinx/cdoc.py new file mode 100755 index 0000000000..318e9b2362 --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/sphinx/cdoc.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python +# SPDX_License-Identifier: MIT +# +# Copyright (C) 2018 Luc Van Oostenryck <luc.vanoostenryck@gmail.com> +# + +""" +/// +// Sparse source files may contain documentation inside block-comments +// specifically formatted:: +// +// /// +// // Here is some doc +// // and here is some more. +// +// More precisely, a doc-block begins with a line containing only ``///`` +// and continues with lines beginning by ``//`` followed by either a space, +// a tab or nothing, the first space after ``//`` is ignored. +// +// For functions, some additional syntax must be respected inside the +// block-comment:: +// +// /// +// // <mandatory short one-line description> +// // <optional blank line> +// // @<1st parameter's name>: <description> +// // @<2nd parameter's name>: <long description +// // <tab>which needs multiple lines> +// // @return: <description> (absent for void functions) +// // <optional blank line> +// // <optional long multi-line description> +// int somefunction(void *ptr, int count); +// +// Inside the description fields, parameter's names can be referenced +// by using ``@<parameter name>``. A function doc-block must directly precede +// the function it documents. This function can span multiple lines and +// can either be a function prototype (ending with ``;``) or a +// function definition. +// +// Some future versions will also allow to document structures, unions, +// enums, typedefs and variables. +// +// This documentation can be extracted into a .rst document by using +// the *autodoc* directive:: +// +// .. c:autodoc:: file.c +// + +""" + +import re + +class Lines: + def __init__(self, lines): + # type: (Iterable[str]) -> None + self.index = 0 + self.lines = lines + self.last = None + self.back = False + + def __iter__(self): + # type: () -> Lines + return self + + def memo(self): + # type: () -> Tuple[int, str] + return (self.index, self.last) + + def __next__(self): + # type: () -> Tuple[int, str] + if not self.back: + self.last = next(self.lines).rstrip() + self.index += 1 + else: + self.back = False + return self.memo() + def next(self): + return self.__next__() + + def undo(self): + # type: () -> None + self.back = True + +def readline_multi(lines, line): + # type: (Lines, str) -> str + try: + while True: + (n, l) = next(lines) + if not l.startswith('//\t'): + raise StopIteration + line += '\n' + l[3:] + except: + lines.undo() + return line + +def readline_delim(lines, delim): + # type: (Lines, Tuple[str, str]) -> Tuple[int, str] + try: + (lineno, line) = next(lines) + if line == '': + raise StopIteration + while line[-1] not in delim: + (n, l) = next(lines) + line += ' ' + l.lstrip() + except: + line = '' + return (lineno, line) + + +def process_block(lines): + # type: (Lines) -> Dict[str, Any] + info = { } + tags = [] + desc = [] + state = 'START' + + (n, l) = lines.memo() + #print('processing line ' + str(n) + ': ' + l) + + ## is it a single line comment ? + m = re.match(r"^///\s+(.+)$", l) # /// ... + if m: + info['type'] = 'single' + info['desc'] = (n, m.group(1).rstrip()) + return info + + ## read the multi line comment + for (n, l) in lines: + #print('state %d: %4d: %s' % (state, n, l)) + if l.startswith('// '): + l = l[3:] ## strip leading '// ' + elif l.startswith('//\t') or l == '//': + l = l[2:] ## strip leading '//' + else: + lines.undo() ## end of doc-block + break + + if state == 'START': ## one-line short description + info['short'] = (n ,l) + state = 'PRE-TAGS' + elif state == 'PRE-TAGS': ## ignore empty line + if l != '': + lines.undo() + state = 'TAGS' + elif state == 'TAGS': ## match the '@tagnames' + m = re.match(r"^@([\w-]*)(:?\s*)(.*)", l) + if m: + tag = m.group(1) + sep = m.group(2) + ## FIXME/ warn if sep != ': ' + l = m.group(3) + l = readline_multi(lines, l) + tags.append((n, tag, l)) + else: + lines.undo() + state = 'PRE-DESC' + elif state == 'PRE-DESC': ## ignore the first empty lines + if l != '': ## or first line of description + desc = [n, l] + state = 'DESC' + elif state == 'DESC': ## remaining lines -> description + desc.append(l) + else: + pass + + ## fill the info + if len(tags): + info['tags'] = tags + if len(desc): + info['desc'] = desc + + ## read the item (function only for now) + (n, line) = readline_delim(lines, (')', ';')) + if len(line): + line = line.rstrip(';') + #print('function: %4d: %s' % (n, line)) + info['type'] = 'func' + info['func'] = (n, line) + else: + info['type'] = 'bloc' + + return info + +def process_file(f): + # type: (TextIOWrapper) -> List[Dict[str, Any]] + docs = [] + lines = Lines(f) + for (n, l) in lines: + #print("%4d: %s" % (n, l)) + if l.startswith('///'): + info = process_block(lines) + docs.append(info) + + return docs + +def decorate(l): + # type: (str) -> str + l = re.sub(r"@(\w+)", "**\\1**", l) + return l + +def convert_to_rst(info): + # type: (Dict[str, Any]) -> List[Tuple[int, str]] + lst = [] + #print('info= ' + str(info)) + typ = info.get('type', '???') + if typ == '???': + ## uh ? + pass + elif typ == 'bloc': + if 'short' in info: + (n, l) = info['short'] + lst.append((n, l)) + if 'desc' in info: + desc = info['desc'] + n = desc[0] - 1 + desc.append('') + for i in range(1, len(desc)): + l = desc[i] + lst.append((n+i, l)) + # auto add a blank line for a list + if re.search(r":$", desc[i]) and re.search(r"\S", desc[i+1]): + lst.append((n+i, '')) + + elif typ == 'func': + (n, l) = info['func'] + l = '.. c:function:: ' + l + lst.append((n, l + '\n')) + if 'short' in info: + (n, l) = info['short'] + l = l[0].capitalize() + l[1:].strip('.') + l = '\t' + l + '.' + lst.append((n, l + '\n')) + if 'tags' in info: + for (n, name, l) in info.get('tags', []): + if name != 'return': + name = 'param ' + name + l = decorate(l) + l = '\t:%s: %s' % (name, l) + l = '\n\t\t'.join(l.split('\n')) + lst.append((n, l)) + lst.append((n+1, '')) + if 'desc' in info: + desc = info['desc'] + n = desc[0] + r = '' + for l in desc[1:]: + l = decorate(l) + r += '\t' + l + '\n' + lst.append((n, r)) + return lst + +def extract(f, filename): + # type: (TextIOWrapper, str) -> List[Tuple[int, str]] + res = process_file(f) + res = [ i for r in res for i in convert_to_rst(r) ] + return res + +def dump_doc(lst): + # type: (List[Tuple[int, str]]) -> None + for (n, lines) in lst: + for l in lines.split('\n'): + print('%4d: %s' % (n, l)) + n += 1 + +if __name__ == '__main__': + """ extract the doc from stdin """ + import sys + + dump_doc(extract(sys.stdin, '<stdin>')) + + +from sphinx.ext.autodoc import AutodocReporter +import docutils +import os +class CDocDirective(docutils.parsers.rst.Directive): + required_argument = 1 + optional_arguments = 1 + has_content = False + option_spec = { + } + + def run(self): + env = self.state.document.settings.env + filename = os.path.join(env.config.cdoc_srcdir, self.arguments[0]) + env.note_dependency(os.path.abspath(filename)) + + ## create a (view) list from the extracted doc + lst = docutils.statemachine.ViewList() + f = open(filename, 'r') + for (lineno, lines) in extract(f, filename): + for l in lines.split('\n'): + lst.append(l.expandtabs(8), filename, lineno) + lineno += 1 + + ## let parse this new reST content + memo = self.state.memo + save = memo.reporter, memo.title_styles, memo.section_level + memo.reporter = AutodocReporter(lst, memo.reporter) + node = docutils.nodes.section() + try: + self.state.nested_parse(lst, 0, node, match_titles=1) + finally: + memo.reporter, memo.title_styles, memo.section_level = save + return node.children + +def setup(app): + app.add_config_value('cdoc_srcdir', None, 'env') + app.add_directive_to_domain('c', 'autodoc', CDocDirective) + + return { + 'version': '1.0', + 'parallel_read_safe': True, + } + +# vim: tabstop=4 diff --git a/usr/src/tools/smatch/src/Documentation/sphinx/ir.py b/usr/src/tools/smatch/src/Documentation/sphinx/ir.py new file mode 100755 index 0000000000..3028200a67 --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/sphinx/ir.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# SPDX_License-Identifier: MIT +# +# Copyright (C) 2018 Luc Van Oostenryck <luc.vanoostenryck@gmail.com> +# + +""" +/// +// To document the instructions used in the intermediate representation +// a new domain is defined: 'ir' with a directive:: +// +// .. op: <OP_NAME> +// <description of OP_NAME> +// ... +// +// This is equivalent to using a definition list but with the name +// also placed in the index (with 'IR instruction' as descriptions). + +""" + +import docutils +import sphinx + +class IROpDirective(docutils.parsers.rst.Directive): + + # use the first line of content as the argument, this allow + # to not have to write a blanck line after the directive + final_argument_whitespace = True + required_argument = 0 + #optional_arguments = 0 + has_content = True + + objtype = None + + def run(self): + self.env = self.state.document.settings.env + + source = self.state.document + lineno = self.lineno + text = self.content + name = text[0] + + node = docutils.nodes.section() + node['ids'].append(name) + node.document = source + + index = '.. index:: pair: %s; IR instruction' % name + content = docutils.statemachine.ViewList() + content.append(index, source, lineno) + content.append('' , source, lineno) + content.append(name , source, lineno) + content.append('' , source, lineno) + self.state.nested_parse(content, self.content_offset, node) + + defnode = docutils.nodes.definition() + self.state.nested_parse(text[1:], self.content_offset, defnode) + node.append(defnode) + + return [node] + +class IRDomain(sphinx.domains.Domain): + + """IR domain.""" + name = 'ir' + +def setup(app): + app.add_domain(IRDomain) + app.add_directive_to_domain('ir', 'op', IROpDirective) + + return { + 'version': '1.0', + 'parallel_read_safe': True, + } + +# vim: tabstop=4 diff --git a/usr/src/tools/smatch/src/Documentation/test-suite b/usr/src/tools/smatch/src/Documentation/test-suite deleted file mode 100644 index b0c8f134b3..0000000000 --- a/usr/src/tools/smatch/src/Documentation/test-suite +++ /dev/null @@ -1,136 +0,0 @@ - - - Sparse test suite - ~~~~~~~~~~~~~~~~~ - -Sparse has a number of test cases in its validation directory. The test-suite -script aims at making automated checking of these tests possible. It works by -embedding tags in C comments in the test cases. - -check-name: (mandatory) - Name of the test. - -check-description: (optional) - A description of what the test checks. - -check-command: (optional) - There are different kinds of tests. Some can validate the sparse - preprocessor, while others will use sparse, cgcc, or even other backends - of the library. check-command allows you to give a custom command to - run the test-case. - The '$file' string is special. It will be expanded to the file name at - run time. - It defaults to "sparse $file". - -check-exit-value: (optional) - The expected exit value of check-command. It defaults to 0. - -check-timeout: (optional) - The maximum expected duration of check-command, in seconds. - It defaults to 1. - -check-output-start / check-output-end (optional) - The expected output (stdout and stderr) of check-command lies between - those two tags. It defaults to no output. - -check-output-ignore / check-error-ignore (optional) - Don't check the expected output (stdout or stderr) of check-command - (useful when this output is not comparable or if you're only interested - in the exit value). - By default this check is done. - -check-known-to-fail (optional) - Mark the test as being known to fail. - -check-output-contains: <pattern> (optional) - Check that the output (stdout) contains the given pattern. - Several such tags can be given, in which case the output - must contains all the patterns. - -check-output-excludes: <pattern> (optional) - Similar than the above one, but with opposite logic. - Check that the output (stdout) doesn't contain the given pattern. - Several such tags can be given, in which case the output - must contains none of the patterns. - -check-output-pattern-<nbr>-times: <pattern> (optional) - Similar to the contains/excludes above, but with full control - of the number of times the pattern should occur in the output. - - Using test-suite - ~~~~~~~~~~~~~~~~ - -The test-suite script is called through the check target of the Makefile. It -will try to check every test case it finds (find validation -name '*.c'). - -It can be called to check a single test with: -$ cd validation -$ ./test-suite single preprocessor/preprocessor1.c - TEST Preprocessor #1 (preprocessor/preprocessor1.c) -preprocessor/preprocessor1.c passed ! - - - Writing a test - ~~~~~~~~~~~~~~ - -test-suite comes with a format command to make a test easier to write: - - test-suite format file [name [cmd]] - -name: - check-name value. If no name is provided, it defaults to the file name. -cmd: - check-command value. If no cmd is provided, it defaults to - "sparse $file". - -The output of the test-suite format command can be redirected into the -test case to create a test-suite formatted file. - -$ ./test-suite format bad-assignment.c Assignment >> bad-assignment.c -$ cat !$ -cat bad-assignment.c -/* - * check-name: bad assignment - * - * check-command: sparse $file - * check-exit-value: 1 - * - * check-output-start -bad-assignment.c:3:6: error: Expected ; at end of statement -bad-assignment.c:3:6: error: got \ - * check-output-end - */ - -You can define the check-command you want to use for the test. $file will be -extended to the file name at run time. - -$ ./test-suite format validation/preprocessor2.c "Preprocessor #2" \ - "sparse -E \$file" >> validation/preprocessor2.c -$ cat !$ -cat validation/preprocessor2.c -/* - * This one we happen to get right. - * - * It should result in a simple - * - * a + b - * - * for a proper preprocessor. - */ -#define TWO a, b - -#define UNARY(x) BINARY(x) -#define BINARY(x, y) x + y - -UNARY(TWO) -/* - * check-name: Preprocessor #2 - * - * check-command: sparse -E $file - * check-exit-value: 0 - * - * check-output-start - -a + b - * check-output-end - */ diff --git a/usr/src/tools/smatch/src/Documentation/test-suite.rst b/usr/src/tools/smatch/src/Documentation/test-suite.rst new file mode 100644 index 0000000000..333106ee13 --- /dev/null +++ b/usr/src/tools/smatch/src/Documentation/test-suite.rst @@ -0,0 +1,169 @@ +Test suite +########## + +Sparse has a number of test cases in its validation directory. The test-suite +script aims at making automated checking of these tests possible. It works by +embedding tags in C comments in the test cases. + +Tag's syntax +============ + +``check-name:`` *name* + + Name of the test. This is the only mandatory tag. + +``check-description:`` *description ...* + + A description of what the test checks. + +``check-command:`` *command arg ...* + + There are different kinds of tests. Some can validate the sparse + preprocessor, while others will use sparse, cgcc, or even other backends + of the library. check-command allows you to give a custom command to + run the test-case. + The ``$file`` string is special. It will be expanded to the file name at + run time. + It defaults to ``sparse $file``. + +``check-arch-ignore:`` *arch[|...]* + +``check-arch-only:`` *arch[|...]* + + Ignore the test if the current architecture (as returned by ``uname -m``) + matches or not one of the archs given in the pattern. + +``check-assert:`` *condition* + + Ignore the test if the given condition is false when evaluated as a + static assertion (``_Static_assert``). + +``check-cpp-if:`` *condition* + + Ignore the test if the given condition is false when evaluated + by sparse's pre-processor. + +``check-exit-value:`` *value* + + The expected exit value of check-command. It defaults to 0. + +``check-timeout:`` *timeout* + + The maximum expected duration of check-command, in seconds. + It defaults to 1. + +``check-output-start`` / ``check-output-end`` + + The expected output (stdout and stderr) of check-command lies between + those two tags. It defaults to no output. + +``check-output-ignore`` / ``check-error-ignore`` + + Don't check the expected output (stdout or stderr) of check-command + (useful when this output is not comparable or if you're only interested + in the exit value). By default this check is done. + +``check-known-to-fail`` + + Mark the test as being known to fail. + +``check-output-contains:`` *pattern* + + Check that the output (stdout) contains the given pattern. + Several such tags can be given, in which case the output + must contains all the patterns. + +``check-output-excludes:`` *pattern* + + Similar than the above one, but with opposite logic. + Check that the output (stdout) doesn't contain the given pattern. + Several such tags can be given, in which case the output + must contains none of the patterns. + +``check-output-pattern(``\ *nbr*\ ``):`` *pattern* + +``check-output-pattern(``\ *min*\ ``,``\ *max*\ ``):`` *pattern* + + Similar to the contains/excludes above, but with full control + of the number of times the pattern should occur in the output. + If *min* or *max* is ``-`` the corresponding check is ignored. + +Using test-suite +================ + +The test-suite script is called through the check target of the Makefile. It +will try to check every test case it finds (``find validation -name '*.c'``). +It can be called to check a single test with:: + + $ cd validation + $ ./test-suite single preprocessor/preprocessor1.c + TEST Preprocessor #1 (preprocessor/preprocessor1.c) + preprocessor/preprocessor1.c passed ! + + +Writing a test +============== + +The test-suite comes with a format command to make a test easier to write:: + + test-suite format [-a] [-l] [-f] file [name [cmd]] + +`name:` check-name value + If no name is provided, it defaults to the file name. + +`cmd:` check-command value + If no cmd is provided, it defaults to ``sparse $file``. + +The output of the test-suite format command can be redirected into the +test case to create a test-suite formatted file.:: + + $ ./test-suite format bad-assignment.c Assignment >> bad-assignment.c + $ cat !$ + cat bad-assignment.c + /* + * check-name: bad assignment + * + * check-command: sparse $file + * check-exit-value: 1 + * + * check-output-start + bad-assignment.c:3:6: error: Expected ; at end of statement + bad-assignment.c:3:6: error: got \ + * check-output-end + */ + +The same effect without the redirection can be achieved by using the ``-a`` +option. + +You can define the check-command you want to use for the test.:: + + $ ./test-suite format -a validation/preprocessor2.c "Preprocessor #2" \ + "sparse -E \$file" + $ cat !$ + cat validation/preprocessor2.c + /* + * This one we happen to get right. + * + * It should result in a simple + * + * a + b + * + * for a proper preprocessor. + */ + #define TWO a, b + + #define UNARY(x) BINARY(x) + #define BINARY(x, y) x + y + + UNARY(TWO) + /* + * check-name: Preprocessor #2 + * + * check-command: sparse -E $file + * check-exit-value: 0 + * + * check-output-start + + a + b + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/Makefile b/usr/src/tools/smatch/src/Makefile index 43e67cd759..495cc6192d 100644 --- a/usr/src/tools/smatch/src/Makefile +++ b/usr/src/tools/smatch/src/Makefile @@ -1,318 +1,390 @@ -VERSION=0.5.1-il-6 - -# Generating file version.h if current version has changed -SPARSE_VERSION:=$(shell git describe 2>/dev/null || echo '$(VERSION)') -VERSION_H := $(shell cat version.h 2>/dev/null) -ifneq ($(lastword $(VERSION_H)),"$(SPARSE_VERSION)") -$(info $(shell echo ' GEN 'version.h)) -$(shell echo '#define SPARSE_VERSION "$(SPARSE_VERSION)"' > version.h) -endif +VERSION=0.6.1-rc1-il-1 +######################################################################## +# The following variables can be overwritten from the command line OS = linux -ifeq ($(CC),"") -CC = gcc -endif -CFLAGS += -O2 -finline-functions -fno-strict-aliasing -g -CFLAGS += -Wall -Wwrite-strings -Wno-switch -LDFLAGS += -g -lm -lsqlite3 -lssl -lcrypto -LD = gcc +CC ?= gcc +LD = $(CC) AR = ar -PKG_CONFIG = pkg-config -COMMON_CFLAGS = -O2 -finline-functions -fno-strict-aliasing -g -COMMON_CFLAGS += -Wall -Wwrite-strings -ALL_CFLAGS = $(COMMON_CFLAGS) $(PKG_CFLAGS) $(CFLAGS) -# +CFLAGS ?= -O2 -g + +DESTDIR ?= +PREFIX ?= $(HOME) +BINDIR ?= $(PREFIX)/bin +MANDIR ?= $(PREFIX)/share/man + +PKG_CONFIG ?= pkg-config + +CHECKER_FLAGS ?= -Wno-vla + +# Allow users to override build settings without dirtying their trees # For debugging, put this in local.mk: # # CFLAGS += -O0 -DDEBUG -g3 -gdwarf-2 # +SPARSE_LOCAL_CONFIG ?= local.mk +-include ${SPARSE_LOCAL_CONFIG} +######################################################################## + + +LIB_OBJS := +LIB_OBJS += allocate.o +LIB_OBJS += builtin.o +LIB_OBJS += char.o +LIB_OBJS += compat-$(OS).o +LIB_OBJS += cse.o +LIB_OBJS += dissect.o +LIB_OBJS += dominate.o +LIB_OBJS += evaluate.o +LIB_OBJS += expand.o +LIB_OBJS += expression.o +LIB_OBJS += flow.o +LIB_OBJS += flowgraph.o +LIB_OBJS += inline.o +LIB_OBJS += ir.o +LIB_OBJS += lib.o +LIB_OBJS += linearize.o +LIB_OBJS += liveness.o +LIB_OBJS += memops.o +LIB_OBJS += opcode.o +LIB_OBJS += optimize.o +LIB_OBJS += parse.o +LIB_OBJS += pre-process.o +LIB_OBJS += ptrlist.o +LIB_OBJS += ptrmap.o +LIB_OBJS += scope.o +LIB_OBJS += show-parse.o +LIB_OBJS += simplify.o +LIB_OBJS += sort.o +LIB_OBJS += ssa.o +LIB_OBJS += sset.o +LIB_OBJS += stats.o +LIB_OBJS += storage.o +LIB_OBJS += symbol.o +LIB_OBJS += target.o +LIB_OBJS += tokenize.o +LIB_OBJS += unssa.o +LIB_OBJS += utils.o +LIB_OBJS += macro_table.o +LIB_OBJS += token_store.o +LIB_OBJS += cwchash/hashtable.o + +PROGRAMS := +PROGRAMS += compile +PROGRAMS += ctags +PROGRAMS += example +PROGRAMS += graph +PROGRAMS += obfuscate +PROGRAMS += sparse +PROGRAMS += test-dissect +PROGRAMS += test-lexing +PROGRAMS += test-linearize +PROGRAMS += test-parsing +PROGRAMS += test-unssa + +INST_PROGRAMS=smatch sparse cgcc +INST_MAN1=sparse.1 cgcc.1 -HAVE_LIBXML:=$(shell $(PKG_CONFIG) --exists libxml-2.0 2>/dev/null && echo 'yes') -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) -GTK_VERSION:=3.0 -HAVE_GTK:=$(shell $(PKG_CONFIG) --exists gtk+-$(GTK_VERSION) 2>/dev/null && echo 'yes') -ifneq ($(HAVE_GTK),yes) - GTK_VERSION:=2.0 - HAVE_GTK:=$(shell $(PKG_CONFIG) --exists gtk+-$(GTK_VERSION) 2>/dev/null && echo 'yes') -endif +all: -LLVM_CONFIG:=llvm-config -HAVE_LLVM:=$(shell $(LLVM_CONFIG) --version >/dev/null 2>&1 && echo 'yes') +######################################################################## +# common flags/options/... + +cflags = -fno-strict-aliasing +cflags += -Wall -Wwrite-strings -Wno-switch GCC_BASE := $(shell $(CC) --print-file-name=) -COMMON_CFLAGS += -DGCC_BASE=\"$(GCC_BASE)\" +cflags += -DGCC_BASE=\"$(GCC_BASE)\" MULTIARCH_TRIPLET := $(shell $(CC) -print-multiarch 2>/dev/null) -COMMON_CFLAGS += -DMULTIARCH_TRIPLET=\"$(MULTIARCH_TRIPLET)\" - -ifeq ($(HAVE_GCC_DEP),yes) -COMMON_CFLAGS += -Wp,-MD,$(@D)/.$(@F).d -endif +cflags += -DMULTIARCH_TRIPLET=\"$(MULTIARCH_TRIPLET)\" -DESTDIR= -INSTALL_PREFIX ?=$(HOME) -BINDIR=$(INSTALL_PREFIX)/bin -LIBDIR=$(INSTALL_PREFIX)/lib -MANDIR=$(INSTALL_PREFIX)/share/man -MAN1DIR=$(MANDIR)/man1 -INCLUDEDIR=$(INSTALL_PREFIX)/include -PKGCONFIGDIR=$(LIBDIR)/pkgconfig -SMATCHDATADIR=$(INSTALL_PREFIX)/share/smatch - -SMATCH_FILES=smatch_flow.o smatch_conditions.o smatch_slist.o smatch_states.o \ - smatch_helper.o smatch_type.o smatch_hooks.o smatch_function_hooks.o \ - smatch_modification_hooks.o smatch_extra.o smatch_estate.o smatch_math.o \ - smatch_sval.o smatch_ranges.o smatch_implied.o smatch_ignore.o smatch_project.o \ - smatch_var_sym.o smatch_tracker.o smatch_files.o smatch_expression_stacks.o \ - smatch_equiv.o smatch_buf_size.o smatch_strlen.o smatch_capped.o smatch_db.o \ - smatch_expressions.o smatch_returns.o smatch_parse_call_math.o \ - smatch_param_limit.o smatch_param_filter.o \ - smatch_param_set.o smatch_comparison.o smatch_param_compare_limit.o smatch_local_values.o \ - smatch_function_ptrs.o smatch_annotate.o smatch_string_list.o \ - smatch_param_cleared.o smatch_start_states.o \ - smatch_recurse.o smatch_data_source.o smatch_type_val.o \ - smatch_common_functions.o smatch_struct_assignment.o \ - smatch_unknown_value.o smatch_stored_conditions.o avl.o \ - smatch_function_info.o smatch_links.o smatch_auto_copy.o \ - smatch_type_links.o smatch_untracked_param.o smatch_impossible.o \ - smatch_strings.o smatch_param_used.o smatch_container_of.o smatch_address.o \ - smatch_buf_comparison.o smatch_real_absolute.o smatch_scope.o \ - smatch_imaginary_absolute.o smatch_parameter_names.o \ - smatch_return_to_param.o smatch_passes_array_size.o \ - smatch_constraints.o smatch_constraints_required.o \ - smatch_fn_arg_link.o smatch_about_fn_ptr_arg.o smatch_mtag.o \ - smatch_mtag_map.o smatch_mtag_data.o \ - smatch_param_to_mtag_data.o smatch_mem_tracker.o smatch_array_values.o \ - smatch_nul_terminator.o smatch_assigned_expr.o smatch_kernel_user_data.o \ - smatch_statement_count.o smatch_integer_overflow.o smatch_bits.o -SMATCH_CHECKS=$(shell ls check_*.c | sed -e 's/\.c/.o/') -SMATCH_DATA=smatch_data/kernel.allocation_funcs \ - smatch_data/kernel.frees_argument smatch_data/kernel.puts_argument \ - smatch_data/kernel.dev_queue_xmit smatch_data/kernel.returns_err_ptr \ - smatch_data/kernel.dma_funcs smatch_data/kernel.returns_held_funcs \ - smatch_data/kernel.no_return_funcs +bindir := $(DESTDIR)$(BINDIR) +man1dir := $(DESTDIR)$(MANDIR)/man1 -SMATCH_SCRIPTS=smatch_scripts/add_gfp_to_allocations.sh \ - smatch_scripts/build_kernel_data.sh \ - smatch_scripts/call_tree.pl smatch_scripts/filter_kernel_deref_check.sh \ - smatch_scripts/find_expanded_holes.pl smatch_scripts/find_null_params.sh \ - smatch_scripts/follow_params.pl smatch_scripts/gen_allocation_list.sh \ - smatch_scripts/gen_bit_shifters.sh smatch_scripts/gen_dma_funcs.sh \ - smatch_scripts/generisize.pl smatch_scripts/gen_err_ptr_list.sh \ - smatch_scripts/gen_expects_err_ptr.sh smatch_scripts/gen_frees_list.sh \ - smatch_scripts/gen_gfp_flags.sh smatch_scripts/gen_no_return_funcs.sh \ - smatch_scripts/gen_puts_list.sh smatch_scripts/gen_returns_held.sh \ - smatch_scripts/gen_rosenberg_funcs.sh smatch_scripts/gen_sizeof_param.sh \ - smatch_scripts/gen_unwind_functions.sh smatch_scripts/kchecker \ - smatch_scripts/kpatch.sh smatch_scripts/new_bugs.sh \ - smatch_scripts/show_errs.sh smatch_scripts/show_ifs.sh \ - smatch_scripts/show_unreachable.sh smatch_scripts/strip_whitespace.pl \ - smatch_scripts/summarize_errs.sh smatch_scripts/test_kernel.sh \ - smatch_scripts/trace_params.pl smatch_scripts/unlocked_paths.pl \ - smatch_scripts/whitespace_only.sh smatch_scripts/wine_checker.sh \ +######################################################################## +# target specificities -PROGRAMS=test-lexing test-parsing obfuscate compile graph sparse \ - test-linearize example test-unssa test-dissect ctags -INST_PROGRAMS=smatch cgcc +compile: compile-i386.o +EXTRA_OBJS += compile-i386.o -INST_MAN1=sparse.1 cgcc.1 +# Can we use GCC's generated dependencies? +HAVE_GCC_DEP:=$(shell touch .gcc-test.c && \ + $(CC) -c -Wp,-MP,-MMD,.gcc-test.d .gcc-test.c 2>/dev/null && \ + echo 'yes'; rm -f .gcc-test.d .gcc-test.o .gcc-test.c) +ifeq ($(HAVE_GCC_DEP),yes) +cflags += -Wp,-MP,-MMD,$(@D)/.$(@F).d +endif +# Can we use libxml (needed for c2xml)? +HAVE_LIBXML:=$(shell $(PKG_CONFIG) --exists libxml-2.0 2>/dev/null && echo 'yes') ifeq ($(HAVE_LIBXML),yes) PROGRAMS+=c2xml INST_PROGRAMS+=c2xml -c2xml_EXTRA_OBJS = `$(PKG_CONFIG) --libs libxml-2.0` -LIBXML_CFLAGS := $(shell $(PKG_CONFIG) --cflags libxml-2.0) +c2xml-ldlibs := $(shell $(PKG_CONFIG) --libs libxml-2.0) +c2xml-cflags := $(shell $(PKG_CONFIG) --cflags libxml-2.0) else $(warning Your system does not have libxml, disabling c2xml) endif +# Can we use gtk (needed for test-inspect) +GTK_VERSION:=3.0 +HAVE_GTK:=$(shell $(PKG_CONFIG) --exists gtk+-$(GTK_VERSION) 2>/dev/null && echo 'yes') +ifneq ($(HAVE_GTK),yes) +GTK_VERSION:=2.0 +HAVE_GTK:=$(shell $(PKG_CONFIG) --exists gtk+-$(GTK_VERSION) 2>/dev/null && echo 'yes') +endif ifeq ($(HAVE_GTK),yes) GTK_CFLAGS := $(shell $(PKG_CONFIG) --cflags gtk+-$(GTK_VERSION)) -GTK_LIBS := $(shell $(PKG_CONFIG) --libs gtk+-$(GTK_VERSION)) +ast-view-cflags := $(GTK_CFLAGS) +ast-model-cflags := $(GTK_CFLAGS) +ast-inspect-cflags := $(GTK_CFLAGS) +test-inspect-cflags := $(GTK_CFLAGS) +test-inspect-ldlibs := $(shell $(PKG_CONFIG) --libs gtk+-$(GTK_VERSION)) +test-inspect: ast-model.o ast-view.o ast-inspect.o +EXTRA_OBJS += ast-model.o ast-view.o ast-inspect.o PROGRAMS += test-inspect INST_PROGRAMS += test-inspect -test-inspect_EXTRA_DEPS := ast-model.o ast-view.o ast-inspect.o -test-inspect_OBJS := test-inspect.o $(test-inspect_EXTRA_DEPS) -$(test-inspect_OBJS) $(test-inspect_OBJS:.o=.sc): PKG_CFLAGS += $(GTK_CFLAGS) -test-inspect_EXTRA_OBJS := $(GTK_LIBS) else $(warning Your system does not have gtk3/gtk2, disabling test-inspect) endif +# Can we use LLVM (needed for ... sparse-llvm)? +LLVM_CONFIG:=llvm-config +HAVE_LLVM:=$(shell $(LLVM_CONFIG) --version >/dev/null 2>&1 && echo 'yes') ifeq ($(HAVE_LLVM),yes) -ifeq ($(shell uname -m | grep -q '\(i386\|x86\)' && echo ok),ok) +arch := $(shell uname -m) +ifeq (${MULTIARCH_TRIPLET},x86_64-linux-gnux32) +arch := x32 +endif +ifneq ($(filter ${arch},i386 i486 i586 i686 x86_64 amd64),) LLVM_VERSION:=$(shell $(LLVM_CONFIG) --version) ifeq ($(shell expr "$(LLVM_VERSION)" : '[3-9]\.'),2) LLVM_PROGS := sparse-llvm $(LLVM_PROGS): LD := g++ LLVM_LDFLAGS := $(shell $(LLVM_CONFIG) --ldflags) -LLVM_CFLAGS := $(shell $(LLVM_CONFIG) --cflags | sed -e "s/-DNDEBUG//g" | sed -e "s/-pedantic//g") +LLVM_CFLAGS := -I$(shell $(LLVM_CONFIG) --includedir) LLVM_LIBS := $(shell $(LLVM_CONFIG) --libs) LLVM_LIBS += $(shell $(LLVM_CONFIG) --system-libs 2>/dev/null) +LLVM_LIBS += $(shell $(LLVM_CONFIG) --cxxflags | grep -F -q -e '-stdlib=libc++' && echo -lc++) PROGRAMS += $(LLVM_PROGS) INST_PROGRAMS += sparse-llvm sparsec -sparse-llvm.o sparse-llvm.sc: PKG_CFLAGS += $(LLVM_CFLAGS) -sparse-llvm_EXTRA_OBJS := $(LLVM_LIBS) $(LLVM_LDFLAGS) +sparse-llvm-cflags := $(LLVM_CFLAGS) -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS +sparse-llvm-ldflags := $(LLVM_LDFLAGS) +sparse-llvm-ldlibs := $(LLVM_LIBS) else $(warning LLVM 3.0 or later required. Your system has version $(LLVM_VERSION) installed.) endif else -$(warning sparse-llvm disabled on $(shell uname -m)) +$(warning sparse-llvm disabled on ${arch}) endif else $(warning Your system does not have llvm, disabling sparse-llvm) 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 - -LIB_OBJS= target.o parse.o tokenize.o pre-process.o symbol.o lib.o scope.o \ - expression.o show-parse.o evaluate.o expand.o inline.o linearize.o \ - char.o sort.o allocate.o compat-$(OS).o ptrlist.o \ - builtin.o \ - stats.o \ - flow.o cse.o simplify.o memops.o liveness.o storage.o unssa.o \ - dissect.o \ - macro_table.o token_store.o cwchash/hashtable.o - -LIB_FILE= libsparse.a -SLIB_FILE= libsparse.so - -# If you add $(SLIB_FILE) to this, you also need to add -fpic to BASIC_CFLAGS above. -# Doing so incurs a noticeable performance hit, and Sparse does not have a -# stable shared library interface, so this does not occur by default. If you -# really want a shared library, you may want to build Sparse twice: once -# without -fpic to get all the Sparse tools, and again with -fpic to get the -# shared library. -LIBS=$(LIB_FILE) +######################################################################## +LIBS := libsparse.a +OBJS := $(LIB_OBJS) $(EXTRA_OBJS) $(PROGRAMS:%=%.o) -# # Pretty print -# -V = @ -Q = $(V:1=) -QUIET_CC = $(Q:@=@echo ' CC '$@;) -QUIET_CHECK = $(Q:@=@echo ' CHECK '$<;) -QUIET_AR = $(Q:@=@echo ' AR '$@;) -QUIET_GEN = $(Q:@=@echo ' GEN '$@;) -QUIET_LINK = $(Q:@=@echo ' LINK '$@;) -# We rely on the -v switch of install to print 'file -> $install_dir/file' -QUIET_INST_SH = $(Q:@=echo -n ' INSTALL ';) -QUIET_INST = $(Q:@=@echo -n ' INSTALL ';) - -define INSTALL_EXEC - $(QUIET_INST)install -v $1 $(DESTDIR)$2/$1 || exit 1; - -endef - -define INSTALL_FILE - $(QUIET_INST)install -v -m 644 $1 $(DESTDIR)$2/$1 || exit 1; - -endef - -SED_PC_CMD = 's|@version@|$(VERSION)|g; \ - s|@prefix@|$(INSTALL_PREFIX)|g; \ - s|@libdir@|$(LIBDIR)|g; \ - s|@includedir@|$(INCLUDEDIR)|g' - - - -# Allow users to override build settings without dirtying their trees --include local.mk +V := @ +Q := $(V:1=) +######################################################################## -all: $(PROGRAMS) sparse.pc smatch - -all-installable: $(INST_PROGRAMS) $(LIBS) $(LIB_H) sparse.pc - -install: all-installable - $(Q)install -d $(DESTDIR)$(BINDIR) - $(Q)install -d $(DESTDIR)$(LIBDIR) - $(Q)install -d $(DESTDIR)$(MAN1DIR) - $(Q)install -d $(DESTDIR)$(INCLUDEDIR)/sparse - $(Q)install -d $(DESTDIR)$(PKGCONFIGDIR) - $(Q)install -d $(DESTDIR)$(SMATCHDATADIR)/smatch_data - $(Q)install -d $(DESTDIR)$(SMATCHDATADIR)/smatch_scripts - $(foreach f,$(INST_PROGRAMS),$(call INSTALL_EXEC,$f,$(BINDIR))) - $(foreach f,$(INST_MAN1),$(call INSTALL_FILE,$f,$(MAN1DIR))) - $(foreach f,$(LIBS),$(call INSTALL_FILE,$f,$(LIBDIR))) - $(foreach f,$(LIB_H),$(call INSTALL_FILE,$f,$(INCLUDEDIR)/sparse)) - $(call INSTALL_FILE,sparse.pc,$(PKGCONFIGDIR)) - $(foreach f,$(SMATCH_DATA),$(call INSTALL_FILE,$f,$(SMATCHDATADIR))) - $(foreach f,$(SMATCH_SCRIPTS),$(call INSTALL_EXEC,$f,$(SMATCHDATADIR))) - -sparse.pc: sparse.pc.in - $(QUIET_GEN)sed $(SED_PC_CMD) sparse.pc.in > sparse.pc - +SMATCHDATADIR=$(INSTALL_PREFIX)/share/smatch -compile_EXTRA_DEPS = compile-i386.o +SMATCH_OBJS := +SMATCH_OBJS += avl.o +SMATCH_OBJS += smatch_about_fn_ptr_arg.o +SMATCH_OBJS += smatch_address.o +SMATCH_OBJS += smatch_annotate.o +SMATCH_OBJS += smatch_array_values.o +SMATCH_OBJS += smatch_assigned_expr.o +SMATCH_OBJS += smatch_bits.o +SMATCH_OBJS += smatch_buf_comparison.o +SMATCH_OBJS += smatch_buf_size.o +SMATCH_OBJS += smatch_capped.o +SMATCH_OBJS += smatch_common_functions.o +SMATCH_OBJS += smatch_comparison.o +SMATCH_OBJS += smatch_conditions.o +SMATCH_OBJS += smatch_constraints.o +SMATCH_OBJS += smatch_constraints_required.o +SMATCH_OBJS += smatch_container_of.o +SMATCH_OBJS += smatch_data_source.o +SMATCH_OBJS += smatch_db.o +SMATCH_OBJS += smatch_equiv.o +SMATCH_OBJS += smatch_estate.o +SMATCH_OBJS += smatch_expressions.o +SMATCH_OBJS += smatch_expression_stacks.o +SMATCH_OBJS += smatch_extra.o +SMATCH_OBJS += smatch_files.o +SMATCH_OBJS += smatch_flow.o +SMATCH_OBJS += smatch_fn_arg_link.o +SMATCH_OBJS += smatch_function_hooks.o +SMATCH_OBJS += smatch_function_info.o +SMATCH_OBJS += smatch_function_ptrs.o +SMATCH_OBJS += smatch_helper.o +SMATCH_OBJS += smatch_hooks.o +SMATCH_OBJS += smatch_ignore.o +SMATCH_OBJS += smatch_imaginary_absolute.o +SMATCH_OBJS += smatch_implied.o +SMATCH_OBJS += smatch_impossible.o +SMATCH_OBJS += smatch_integer_overflow.o +SMATCH_OBJS += smatch_kernel_user_data.o +SMATCH_OBJS += smatch_links.o +SMATCH_OBJS += smatch_math.o +SMATCH_OBJS += smatch_mem_tracker.o +SMATCH_OBJS += smatch_modification_hooks.o +SMATCH_OBJS += smatch_mtag_data.o +SMATCH_OBJS += smatch_mtag_map.o +SMATCH_OBJS += smatch_mtag.o +SMATCH_OBJS += smatch_nul_terminator.o +SMATCH_OBJS += smatch_param_cleared.o +SMATCH_OBJS += smatch_param_compare_limit.o +SMATCH_OBJS += smatch_parameter_names.o +SMATCH_OBJS += smatch_param_filter.o +SMATCH_OBJS += smatch_param_limit.o +SMATCH_OBJS += smatch_param_set.o +SMATCH_OBJS += smatch_param_to_mtag_data.o +SMATCH_OBJS += smatch_param_used.o +SMATCH_OBJS += smatch_parse_call_math.o +SMATCH_OBJS += smatch_passes_array_size.o +SMATCH_OBJS += smatch_project.o +SMATCH_OBJS += smatch_ranges.o +SMATCH_OBJS += smatch_real_absolute.o +SMATCH_OBJS += smatch_recurse.o +SMATCH_OBJS += smatch_returns.o +SMATCH_OBJS += smatch_return_to_param.o +SMATCH_OBJS += smatch_scope.o +SMATCH_OBJS += smatch_slist.o +SMATCH_OBJS += smatch_start_states.o +SMATCH_OBJS += smatch_statement_count.o +SMATCH_OBJS += smatch_states.o +SMATCH_OBJS += smatch_stored_conditions.o +SMATCH_OBJS += smatch_string_list.o +SMATCH_OBJS += smatch_strings.o +SMATCH_OBJS += smatch_strlen.o +SMATCH_OBJS += smatch_struct_assignment.o +SMATCH_OBJS += smatch_sval.o +SMATCH_OBJS += smatch_tracker.o +SMATCH_OBJS += smatch_type_links.o +SMATCH_OBJS += smatch_type.o +SMATCH_OBJS += smatch_type_val.o +SMATCH_OBJS += smatch_unknown_value.o +SMATCH_OBJS += smatch_untracked_param.o +SMATCH_OBJS += smatch_var_sym.o -$(foreach p,$(PROGRAMS),$(eval $(p): $($(p)_EXTRA_DEPS) $(LIBS))) -$(PROGRAMS): % : %.o - $(QUIET_LINK)$(LD) -o $@ $^ $($@_EXTRA_OBJS) $(LDFLAGS) +SMATCH_CHECKS=$(shell ls check_*.c | sed -e 's/\.c/.o/') +SMATCH_DATA=smatch_data/kernel.allocation_funcs \ + smatch_data/kernel.frees_argument smatch_data/kernel.puts_argument \ + smatch_data/kernel.dev_queue_xmit smatch_data/kernel.returns_err_ptr \ + smatch_data/kernel.dma_funcs smatch_data/kernel.returns_held_funcs \ + smatch_data/kernel.no_return_funcs -smatch: smatch.o $(SMATCH_FILES) $(SMATCH_CHECKS) $(LIBS) - $(QUIET_LINK)$(LD) -o $@ $< $(SMATCH_FILES) $(SMATCH_CHECKS) $(LIBS) $(LDFLAGS) +SMATCH_SCRIPTS=smatch_scripts/add_gfp_to_allocations.sh \ + smatch_scripts/build_kernel_data.sh \ + smatch_scripts/call_tree.pl smatch_scripts/filter_kernel_deref_check.sh \ + smatch_scripts/find_expanded_holes.pl smatch_scripts/find_null_params.sh \ + smatch_scripts/follow_params.pl smatch_scripts/gen_allocation_list.sh \ + smatch_scripts/gen_bit_shifters.sh smatch_scripts/gen_dma_funcs.sh \ + smatch_scripts/generisize.pl smatch_scripts/gen_err_ptr_list.sh \ + smatch_scripts/gen_expects_err_ptr.sh smatch_scripts/gen_frees_list.sh \ + smatch_scripts/gen_gfp_flags.sh smatch_scripts/gen_no_return_funcs.sh \ + smatch_scripts/gen_puts_list.sh smatch_scripts/gen_returns_held.sh \ + smatch_scripts/gen_rosenberg_funcs.sh smatch_scripts/gen_sizeof_param.sh \ + smatch_scripts/gen_unwind_functions.sh smatch_scripts/kchecker \ + smatch_scripts/kpatch.sh smatch_scripts/new_bugs.sh \ + smatch_scripts/show_errs.sh smatch_scripts/show_ifs.sh \ + smatch_scripts/show_unreachable.sh smatch_scripts/strip_whitespace.pl \ + smatch_scripts/summarize_errs.sh smatch_scripts/test_kernel.sh \ + smatch_scripts/trace_params.pl smatch_scripts/unlocked_paths.pl \ + smatch_scripts/whitespace_only.sh smatch_scripts/wine_checker.sh \ -$(LIB_FILE): $(LIB_OBJS) - $(QUIET_AR)$(AR) rcs $@ $(LIB_OBJS) +SMATCH_LDFLAGS := -lsqlite3 -lssl -lcrypto -lm -$(SLIB_FILE): $(LIB_OBJS) - $(QUIET_LINK)$(CC) -Wl,-soname,$@ -shared -o $@ $(LIB_OBJS) $(LDFLAGS) +smatch: smatch.o $(SMATCH_OBJS) $(SMATCH_CHECKS) $(LIBS) + $(Q)$(LD) -o $@ $< $(SMATCH_OBJS) $(SMATCH_CHECKS) $(LIBS) $(SMATCH_LDFLAGS) check_list_local.h: touch check_list_local.h smatch.o: smatch.c $(LIB_H) smatch.h check_list.h check_list_local.h $(CC) $(CFLAGS) -c smatch.c -DSMATCHDATADIR='"$(SMATCHDATADIR)"' -$(SMATCH_CHECKS): smatch.h smatch_slist.h smatch_extra.h avl.h -DEP_FILES := $(wildcard .*.o.d) -ifneq ($(DEP_FILES),) -include $(DEP_FILES) -endif +$(SMATCH_OBJS) $(SMATCH_CHECKS): smatch.h smatch_slist.h smatch_extra.h avl.h -c2xml.o c2xml.sc: PKG_CFLAGS += $(LIBXML_CFLAGS) +######################################################################## +all: $(PROGRAMS) smatch -pre-process.sc: CHECKER_FLAGS += -Wno-vla +ldflags += $($(@)-ldflags) $(LDFLAGS) +ldlibs += $($(@)-ldlibs) $(LDLIBS) -lm +$(PROGRAMS): % : %.o $(LIBS) + @echo " LD $@" + $(Q)$(LD) $(ldflags) $^ $(ldlibs) -o $@ -%.o: %.c $(LIB_H) - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< +libsparse.a: $(LIB_OBJS) + @echo " AR $@" + $(Q)$(AR) rcs $@ $^ -%.sc: %.c sparse - $(QUIET_CHECK) $(CHECKER) $(CHECKER_FLAGS) -c $(ALL_CFLAGS) $< -ALL_OBJS := $(LIB_OBJS) $(foreach p,$(PROGRAMS),$(p).o $($(p)_EXTRA_DEPS)) -selfcheck: $(ALL_OBJS:.o=.sc) +cflags += $($(*)-cflags) $(CPPFLAGS) $(CFLAGS) +%.o: %.c + @echo " CC $@" + $(Q)$(CC) $(cflags) -c -o $@ $< +%.sc: %.c sparse + @echo " CHECK $<" + $(Q)CHECK=./sparse ./cgcc -no-compile $(CHECKER_FLAGS) $(cflags) -c $< + +selfcheck: $(OBJS:.o=.sc) -clean: clean-check - rm -f *.[oa] .*.d *.so cwchash/*.o cwchash/.*.d cwchash/tester \ - $(PROGRAMS) $(SLIB_FILE) pre-process.h sparse.pc version.h -dist: - @if test "$(SPARSE_VERSION)" != "v$(VERSION)" ; then \ - echo 'Update VERSION in the Makefile before running "make dist".' ; \ - exit 1 ; \ +SPARSE_VERSION:=$(shell git describe --dirty 2>/dev/null || echo '$(VERSION)') +lib.o: version.h +version.h: FORCE + @echo '#define SPARSE_VERSION "$(SPARSE_VERSION)"' > version.h.tmp + @if cmp -s version.h version.h.tmp; then \ + rm version.h.tmp; \ + else \ + echo " GEN $@"; \ + mv version.h.tmp version.h; \ fi - git archive --format=tar --prefix=sparse-$(VERSION)/ HEAD^{tree} | gzip -9 > sparse-$(VERSION).tar.gz + check: all $(Q)cd validation && ./test-suite +validation/%.t: $(PROGRAMS) + @validation/test-suite single $*.c + +clean: clean-check + @rm -f *.[oa] .*.d $(PROGRAMS) version.h smatch clean-check: - find validation/ \( -name "*.c.output.expected" \ - -o -name "*.c.output.got" \ - -o -name "*.c.output.diff" \ - -o -name "*.c.error.expected" \ - -o -name "*.c.error.got" \ - -o -name "*.c.error.diff" \ - \) -exec rm {} \; + @echo " CLEAN" + @find validation/ \( -name "*.c.output.*" \ + -o -name "*.c.error.*" \ + -o -name "*.o" \ + \) -exec rm {} \; + + +install: install-bin install-man +install-bin: $(INST_PROGRAMS:%=$(bindir)/%) +install-man: $(INST_MAN1:%=$(man1dir)/%) + +$(bindir)/%: % + @echo " INSTALL $@" + $(Q)install -D $< $@ || exit 1; +$(man1dir)/%: % + @echo " INSTALL $@" + $(Q)install -D -m 644 $< $@ || exit 1; + +.PHONY: FORCE + +# GCC's dependencies +-include $(OBJS:%.o=.%.o.d) diff --git a/usr/src/tools/smatch/src/README b/usr/src/tools/smatch/src/README index e350c6c997..b099dae902 100644 --- a/usr/src/tools/smatch/src/README +++ b/usr/src/tools/smatch/src/README @@ -1,3 +1,72 @@ -There are some documents under the Documentation/ directory. - For parsing implicit dependencies, see smatch_scripts/implicit_dependencies. +======= + sparse (spärs), adj,., spars-er, spars-est. + 1. thinly scattered or distributed; "a sparse population" + 2. thin; not thick or dense: "sparse hair" + 3. scanty; meager. + 4. semantic parse + [ from Latin: spars(us) scattered, past participle of + spargere 'to sparge' ] + + Antonym: abundant + +Sparse is a semantic parser of source files: it's neither a compiler +(although it could be used as a front-end for one) nor is it a +preprocessor (although it contains as a part of it a preprocessing +phase). + +It is meant to be a small - and simple - library. Scanty and meager, +and partly because of that easy to use. It has one mission in life: +create a semantic parse tree for some arbitrary user for further +analysis. It's not a tokenizer, nor is it some generic context-free +parser. In fact, context (semantics) is what it's all about - figuring +out not just what the grouping of tokens are, but what the _types_ are +that the grouping implies. + +And no, it doesn't use lex and yacc (or flex and bison). In my personal +opinion, the result of using lex/yacc tends to end up just having to +fight the assumptions the tools make. + +The parsing is done in five phases: + + - full-file tokenization + - pre-processing (which can cause another tokenization phase of another + file) + - semantic parsing. + - lazy type evaluation + - inline function expansion and tree simplification + +Note the "full file" part. Partly for efficiency, but mostly for ease of +use, there are no "partial results". The library completely parses one +whole source file, and builds up the _complete_ parse tree in memory. + +Also note the "lazy" in the type evaluation. The semantic parsing +itself will know which symbols are typedefines (required for parsing C +correctly), but it will not have calculated what the details of the +different types are. That will be done only on demand, as the back-end +requires the information. + +This means that a user of the library will literally just need to do + + struct string_list *filelist = NULL; + char *file; + + action(sparse_initialize(argc, argv, filelist)); + + FOR_EACH_PTR(filelist, file) { + action(sparse(file)); + } END_FOR_EACH_PTR(file); + +and he is now done - having a full C parse of the file he opened. The +library doesn't need any more setup, and once done does not impose any +more requirements. The user is free to do whatever he wants with the +parse tree that got built up, and needs not worry about the library ever +again. There is no extra state, there are no parser callbacks, there is +only the parse tree that is described by the header files. The action +funtion takes a pointer to a symbol_list and does whatever it likes with it. + +The library also contains (as an example user) a few clients that do the +preprocessing, parsing and type evaluation and just print out the +results. These clients were done to verify and debug the library, and +also as trivial examples of what you can do with the parse tree once it +is formed, so that users can see how the tree is organized. diff --git a/usr/src/tools/smatch/src/allocate.c b/usr/src/tools/smatch/src/allocate.c index 502095ef2e..d89336533b 100644 --- a/usr/src/tools/smatch/src/allocate.c +++ b/usr/src/tools/smatch/src/allocate.c @@ -103,6 +103,8 @@ void *allocate(struct allocator_struct *desc, unsigned int size) struct allocation_blob *newblob = blob_alloc(chunking); if (!newblob) die("out of memory"); + if (size > chunking) + die("alloc too big"); desc->total_bytes += chunking; newblob->next = blob; blob = newblob; diff --git a/usr/src/tools/smatch/src/allocate.h b/usr/src/tools/smatch/src/allocate.h index 32987b0800..5df79f3ac0 100644 --- a/usr/src/tools/smatch/src/allocate.h +++ b/usr/src/tools/smatch/src/allocate.h @@ -1,6 +1,8 @@ #ifndef ALLOCATE_H #define ALLOCATE_H +#include "compat.h" + struct allocation_blob { struct allocation_blob *next; unsigned int left, offset; diff --git a/usr/src/tools/smatch/src/bits.h b/usr/src/tools/smatch/src/bits.h new file mode 100644 index 0000000000..c0dc952eae --- /dev/null +++ b/usr/src/tools/smatch/src/bits.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Helper functions for manipulation & testing of integer values + * like zero or sign-extensions. + * + * Copyright (C) 2017 Luc Van Oostenryck + * + */ + +#ifndef BITS_H +#define BITS_H + +static inline unsigned long long sign_bit(unsigned size) +{ + return 1ULL << (size - 1); +} + +static inline unsigned long long sign_mask(unsigned size) +{ + unsigned long long sbit = sign_bit(size); + return sbit - 1; +} + +static inline unsigned long long bits_mask(unsigned size) +{ + unsigned long long sbit = sign_bit(size); + return sbit | (sbit - 1); +} + + +static inline long long zero_extend(long long val, unsigned size) +{ + return val & bits_mask(size); +} + +static inline long long sign_extend(long long val, unsigned size) +{ + if (val & sign_bit(size)) + val |= ~sign_mask(size); + return val; +} + +/// +// sign extend @val but only if exactly representable +static inline long long sign_extend_safe(long long val, unsigned size) +{ + unsigned long long mask = bits_mask(size); + if (!(val & ~mask)) + val = sign_extend(val, size); + return val; +} + +static inline long long bits_extend(long long val, unsigned size, int is_signed) +{ + val = zero_extend(val, size); + if (is_signed) + val = sign_extend(val, size); + return val; +} + +#endif diff --git a/usr/src/tools/smatch/src/builtin.c b/usr/src/tools/smatch/src/builtin.c index 9f90926cb0..221c98991f 100644 --- a/usr/src/tools/smatch/src/builtin.c +++ b/usr/src/tools/smatch/src/builtin.c @@ -24,9 +24,11 @@ */ #include "expression.h" +#include "evaluate.h" #include "expand.h" #include "symbol.h" #include "compat/bswap.h" +#include <stdarg.h> static int evaluate_to_int_const_expr(struct expression *expr) { @@ -50,35 +52,39 @@ static int evaluate_pure_unop(struct expression *expr) return 1; } - -static int evaluate_expect(struct expression *expr) -{ - /* Should we evaluate it to return the type of the first argument? */ - expr->ctype = &int_ctype; - return 1; -} - -static int arguments_choose(struct expression *expr) +/* + * eval_args - check the number of arguments and evaluate them. + */ +static int eval_args(struct expression *expr, int n) { - struct expression_list *arglist = expr->args; struct expression *arg; - int i = 0; - - FOR_EACH_PTR (arglist, arg) { + struct symbol *sym; + const char *msg; + int rc = 1; + + FOR_EACH_PTR(expr->args, arg) { + if (n-- == 0) { + msg = "too many arguments"; + goto error; + } if (!evaluate_expression(arg)) - return 0; - i++; + rc = 0; } END_FOR_EACH_PTR(arg); - if (i < 3) { - sparse_error(expr->pos, - "not enough arguments for __builtin_choose_expr"); - return 0; - } if (i > 3) { - sparse_error(expr->pos, - "too many arguments for __builtin_choose_expr"); - return 0; + if (n > 0) { + msg = "not enough arguments"; + goto error; } - return 1; + return rc; + +error: + sym = expr->fn->ctype; + expression_error(expr, "%s for %s", msg, show_ident(sym->ident)); + return 0; +} + +static int args_triadic(struct expression *expr) +{ + return eval_args(expr, 3); } static int evaluate_choose(struct expression *expr) @@ -185,13 +191,12 @@ static struct symbol_op warning_op = { }; static struct symbol_op expect_op = { - .evaluate = evaluate_expect, .expand = expand_expect }; static struct symbol_op choose_op = { + .args = args_triadic, .evaluate = evaluate_choose, - .args = arguments_choose, }; /* The argument is constant and valid if the cost is zero */ @@ -225,6 +230,92 @@ static struct symbol_op bswap_op = { }; +static int evaluate_fp_unop(struct expression *expr) +{ + struct expression *arg; + + if (!eval_args(expr, 1)) + return 0; + + arg = first_expression(expr->args); + if (!is_float_type(arg->ctype)) { + expression_error(expr, "non-floating-point argument in call to %s()", + show_ident(expr->fn->ctype->ident)); + return 0; + } + return 1; +} + +static struct symbol_op fp_unop_op = { + .evaluate = evaluate_fp_unop, +}; + + +static int evaluate_overflow_gen(struct expression *expr, int ptr) +{ + struct expression *arg; + int n = 0; + + /* there will be exactly 3; we'd already verified that */ + FOR_EACH_PTR(expr->args, arg) { + struct symbol *type; + + n++; + if (!arg || !(type = arg->ctype)) + return 0; + // 1st & 2nd args must be a basic integer type + // 3rd arg must be a pointer to such a type. + if (n == 3 && ptr) { + if (type->type == SYM_NODE) + type = type->ctype.base_type; + if (!type) + return 0; + if (type->type != SYM_PTR) + goto err; + type = type->ctype.base_type; + if (!type) + return 0; + } + if (type->type == SYM_NODE) + type = type->ctype.base_type; + if (!type) + return 0; + if (type->ctype.base_type != &int_type || type == &bool_ctype) + goto err; + } END_FOR_EACH_PTR(arg); + + // the builtin returns a bool + expr->ctype = &bool_ctype; + return 1; + +err: + sparse_error(arg->pos, "invalid type for argument %d:", n); + info(arg->pos, " %s", show_typename(arg->ctype)); + expr->ctype = &bad_ctype; + return 0; +} + +static int evaluate_overflow(struct expression *expr) +{ + return evaluate_overflow_gen(expr, 1); +} + +static struct symbol_op overflow_op = { + .args = args_triadic, + .evaluate = evaluate_overflow, +}; + +static int evaluate_overflow_p(struct expression *expr) +{ + return evaluate_overflow_gen(expr, 0); +} + +static struct symbol_op overflow_p_op = { + .args = args_triadic, + .evaluate = evaluate_overflow_p, +}; + + /* * Builtin functions */ @@ -240,9 +331,21 @@ static struct sym_init { { "__builtin_warning", &builtin_fn_type, MOD_TOPLEVEL, &warning_op }, { "__builtin_expect", &builtin_fn_type, MOD_TOPLEVEL, &expect_op }, { "__builtin_choose_expr", &builtin_fn_type, MOD_TOPLEVEL, &choose_op }, - { "__builtin_bswap16", NULL, MOD_TOPLEVEL, &bswap_op }, - { "__builtin_bswap32", NULL, MOD_TOPLEVEL, &bswap_op }, - { "__builtin_bswap64", NULL, MOD_TOPLEVEL, &bswap_op }, + { "__builtin_bswap16", &builtin_fn_type, MOD_TOPLEVEL, &bswap_op }, + { "__builtin_bswap32", &builtin_fn_type, MOD_TOPLEVEL, &bswap_op }, + { "__builtin_bswap64", &builtin_fn_type, MOD_TOPLEVEL, &bswap_op }, + { "__builtin_isfinite", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, + { "__builtin_isinf", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, + { "__builtin_isinf_sign", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, + { "__builtin_isnan", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, + { "__builtin_isnormal", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, + { "__builtin_signbit", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, + { "__builtin_add_overflow", &builtin_fn_type, MOD_TOPLEVEL, &overflow_op }, + { "__builtin_sub_overflow", &builtin_fn_type, MOD_TOPLEVEL, &overflow_op }, + { "__builtin_mul_overflow", &builtin_fn_type, MOD_TOPLEVEL, &overflow_op }, + { "__builtin_add_overflow_p", &builtin_fn_type, MOD_TOPLEVEL, &overflow_p_op }, + { "__builtin_sub_overflow_p", &builtin_fn_type, MOD_TOPLEVEL, &overflow_p_op }, + { "__builtin_mul_overflow_p", &builtin_fn_type, MOD_TOPLEVEL, &overflow_p_op }, { NULL, NULL, 0 } }; @@ -257,5 +360,199 @@ void init_builtins(int stream) sym->ctype.base_type = ptr->base_type; sym->ctype.modifiers = ptr->modifiers; sym->op = ptr->op; + sym->builtin = 1; + } +} + +static void declare_builtin(const char *name, struct symbol *rtype, int variadic, ...) +{ + int stream = 0; // FIXME + struct symbol *sym = create_symbol(stream, name, SYM_NODE, NS_SYMBOL); + struct symbol *fun = alloc_symbol(sym->pos, SYM_FN); + struct symbol *arg; + va_list args; + + sym->ctype.base_type = fun; + sym->ctype.modifiers = MOD_TOPLEVEL; + sym->builtin = 1; + + fun->ctype.base_type = rtype; + fun->variadic = variadic; + + va_start(args, variadic); + while ((arg = va_arg(args, struct symbol *))) { + struct symbol *anode = alloc_symbol(sym->pos, SYM_NODE); + anode->ctype.base_type = arg; + add_symbol(&fun->arguments, anode); } + va_end(args); +} + +void declare_builtins(void) +{ + struct symbol *va_list_ctype = &ptr_ctype; + + declare_builtin("__builtin_abort", &void_ctype, 0, NULL); + declare_builtin("__builtin_abs", &int_ctype , 0, &int_ctype, NULL); + declare_builtin("__builtin_alloca", &ptr_ctype, 0, size_t_ctype, NULL); + declare_builtin("__builtin_alpha_cmpbge", &long_ctype, 0, &long_ctype, &long_ctype, NULL); + declare_builtin("__builtin_alpha_extbl", &long_ctype, 0, &long_ctype, &long_ctype, NULL); + declare_builtin("__builtin_alpha_extwl", &long_ctype, 0, &long_ctype, &long_ctype, NULL); + declare_builtin("__builtin_alpha_insbl", &long_ctype, 0, &long_ctype, &long_ctype, NULL); + declare_builtin("__builtin_alpha_inslh", &long_ctype, 0, &long_ctype, &long_ctype, NULL); + declare_builtin("__builtin_alpha_insql", &long_ctype, 0, &long_ctype, &long_ctype, NULL); + declare_builtin("__builtin_alpha_inswl", &long_ctype, 0, &long_ctype, &long_ctype, NULL); + declare_builtin("__builtin_bcmp", &int_ctype , 0, &const_ptr_ctype, &const_ptr_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_bcopy", &void_ctype, 0, &const_ptr_ctype, &ptr_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_bswap16", &ushort_ctype, 0, &ushort_ctype, NULL); + declare_builtin("__builtin_bswap32", &uint_ctype, 0, &uint_ctype, NULL); + declare_builtin("__builtin_bswap64", &ullong_ctype, 0, &ullong_ctype, NULL); + declare_builtin("__builtin_bzero", &void_ctype, 0, &ptr_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_calloc", &ptr_ctype, 0, size_t_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_clrsb", &int_ctype, 0, &int_ctype, NULL); + declare_builtin("__builtin_clrsbl", &int_ctype, 0, &long_ctype, NULL); + declare_builtin("__builtin_clrsbll", &int_ctype, 0, &llong_ctype, NULL); + declare_builtin("__builtin_clz", &int_ctype, 0, &int_ctype, NULL); + declare_builtin("__builtin_clzl", &int_ctype, 0, &long_ctype, NULL); + declare_builtin("__builtin_clzll", &int_ctype, 0, &llong_ctype, NULL); + declare_builtin("__builtin_ctz", &int_ctype, 0, &int_ctype, NULL); + declare_builtin("__builtin_ctzl", &int_ctype, 0, &long_ctype, NULL); + declare_builtin("__builtin_ctzll", &int_ctype, 0, &llong_ctype, NULL); + declare_builtin("__builtin_exit", &void_ctype, 0, &int_ctype, NULL); + declare_builtin("__builtin_expect", &long_ctype, 0, &long_ctype ,&long_ctype, NULL); + declare_builtin("__builtin_extract_return_addr", &ptr_ctype, 0, &ptr_ctype, NULL); + declare_builtin("__builtin_fabs", &double_ctype, 0, &double_ctype, NULL); + declare_builtin("__builtin_ffs", &int_ctype, 0, &int_ctype, NULL); + declare_builtin("__builtin_ffsl", &int_ctype, 0, &long_ctype, NULL); + declare_builtin("__builtin_ffsll", &int_ctype, 0, &llong_ctype, NULL); + declare_builtin("__builtin_frame_address", &ptr_ctype, 0, &uint_ctype, NULL); + declare_builtin("__builtin_free", &void_ctype, 0, &ptr_ctype, NULL); + declare_builtin("__builtin_huge_val", &double_ctype, 0, NULL); + declare_builtin("__builtin_huge_valf", &float_ctype, 0, NULL); + declare_builtin("__builtin_huge_vall", &ldouble_ctype, 0, NULL); + declare_builtin("__builtin_index", &string_ctype, 0, &const_string_ctype, &int_ctype, NULL); + declare_builtin("__builtin_inf", &double_ctype, 0, NULL); + declare_builtin("__builtin_inff", &float_ctype, 0, NULL); + declare_builtin("__builtin_infl", &ldouble_ctype, 0, NULL); + declare_builtin("__builtin_isfinite", &int_ctype, 1, NULL); + declare_builtin("__builtin_isgreater", &int_ctype, 0, &float_ctype, &float_ctype, NULL); + declare_builtin("__builtin_isgreaterequal", &int_ctype, 0, &float_ctype, &float_ctype, NULL); + declare_builtin("__builtin_isinf", &int_ctype, 1, NULL); + declare_builtin("__builtin_isinf_sign", &int_ctype, 1, NULL); + declare_builtin("__builtin_isless", &int_ctype, 0, &float_ctype, &float_ctype, NULL); + declare_builtin("__builtin_islessequal", &int_ctype, 0, &float_ctype, &float_ctype, NULL); + declare_builtin("__builtin_islessgreater", &int_ctype, 0, &float_ctype, &float_ctype, NULL); + declare_builtin("__builtin_isnan", &int_ctype, 1, NULL); + declare_builtin("__builtin_isnormal", &int_ctype, 1, NULL); + declare_builtin("__builtin_isunordered", &int_ctype, 0, &float_ctype, &float_ctype, NULL); + declare_builtin("__builtin_labs", &long_ctype, 0, &long_ctype, NULL); + declare_builtin("__builtin_llabs", &llong_ctype, 0, &llong_ctype, NULL); + declare_builtin("__builtin_malloc", &ptr_ctype, 0, size_t_ctype, NULL); + declare_builtin("__builtin_memchr", &ptr_ctype, 0, &const_ptr_ctype, &int_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_memcmp", &int_ctype, 0, &const_ptr_ctype, &const_ptr_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_memcpy", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_memmove", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_mempcpy", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_memset", &ptr_ctype, 0, &ptr_ctype, &int_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_nan", &double_ctype, 0, &const_string_ctype, NULL); + declare_builtin("__builtin_nanf", &float_ctype, 0, &const_string_ctype, NULL); + declare_builtin("__builtin_nanl", &ldouble_ctype, 0, &const_string_ctype, NULL); + declare_builtin("__builtin_object_size", size_t_ctype, 0, &const_ptr_ctype, &int_ctype, NULL); + declare_builtin("__builtin_parity", &int_ctype, 0, &uint_ctype, NULL); + declare_builtin("__builtin_parityl", &int_ctype, 0, &ulong_ctype, NULL); + declare_builtin("__builtin_parityll", &int_ctype, 0, &ullong_ctype, NULL); + declare_builtin("__builtin_popcount", &int_ctype, 0, &uint_ctype, NULL); + declare_builtin("__builtin_popcountl", &int_ctype, 0, &ulong_ctype, NULL); + declare_builtin("__builtin_popcountll", &int_ctype, 0, &ullong_ctype, NULL); + declare_builtin("__builtin_prefetch", &void_ctype, 1, &const_ptr_ctype, NULL); + declare_builtin("__builtin_printf", &int_ctype, 1, &const_string_ctype, NULL); + declare_builtin("__builtin_puts", &int_ctype, 0, &const_string_ctype, NULL); + declare_builtin("__builtin_realloc", &ptr_ctype, 0, &ptr_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_return_address", &ptr_ctype, 0, &uint_ctype, NULL); + declare_builtin("__builtin_rindex", &string_ctype, 0, &const_string_ctype, &int_ctype, NULL); + declare_builtin("__builtin_sadd_overflow", &bool_ctype, 0, &int_ctype, &int_ctype, &int_ptr_ctype, NULL); + declare_builtin("__builtin_saddl_overflow", &bool_ctype, 0, &long_ctype, &long_ctype, &long_ptr_ctype, NULL); + declare_builtin("__builtin_saddll_overflow", &bool_ctype, 0, &llong_ctype, &llong_ctype, &llong_ptr_ctype, NULL); + declare_builtin("__builtin_signbit", &int_ctype, 1, NULL); + declare_builtin("__builtin_smul_overflow", &bool_ctype, 0, &int_ctype, &int_ctype, &int_ptr_ctype, NULL); + declare_builtin("__builtin_smull_overflow", &bool_ctype, 0, &long_ctype, &long_ctype, &long_ptr_ctype, NULL); + declare_builtin("__builtin_smulll_overflow", &bool_ctype, 0, &llong_ctype, &llong_ctype, &llong_ptr_ctype, NULL); + declare_builtin("__builtin_snprintf", &int_ctype, 1, &string_ctype, size_t_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_sprintf", &int_ctype, 1, &string_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_ssub_overflow", &bool_ctype, 0, &int_ctype, &int_ctype, &int_ptr_ctype, NULL); + declare_builtin("__builtin_ssubl_overflow", &bool_ctype, 0, &long_ctype, &long_ctype, &long_ptr_ctype, NULL); + declare_builtin("__builtin_ssubll_overflow", &bool_ctype, 0, &llong_ctype, &llong_ctype, &llong_ptr_ctype, NULL); + declare_builtin("__builtin_stpcpy", &string_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_stpncpy", &string_ctype, 0, &const_string_ctype, &const_string_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_strcasecmp", &int_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_strcasestr", &string_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_strcat", &string_ctype, 0, &string_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_strchr", &string_ctype, 0, &const_string_ctype, &int_ctype, NULL); + declare_builtin("__builtin_strcmp", &int_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_strcpy", &string_ctype, 0, &string_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_strcspn", size_t_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_strdup", &string_ctype, 0, &const_string_ctype, NULL); + declare_builtin("__builtin_strlen", size_t_ctype, 0, &const_string_ctype, NULL); + declare_builtin("__builtin_strncasecmp", &int_ctype, 0, &const_string_ctype, &const_string_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_strncat", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_strncmp", &int_ctype, 0, &const_string_ctype, &const_string_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_strncpy", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_strndup", &string_ctype, 0, &const_string_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_strnstr", &string_ctype, 0, &const_string_ctype, &const_string_ctype, size_t_ctype, NULL); + declare_builtin("__builtin_strpbrk", &string_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_strrchr", &string_ctype, 0, &const_string_ctype, &int_ctype, NULL); + declare_builtin("__builtin_strspn", size_t_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_strstr", &string_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin_trap", &void_ctype, 0, NULL); + declare_builtin("__builtin_uadd_overflow", &bool_ctype, 0, &uint_ctype, &uint_ctype, &uint_ptr_ctype, NULL); + declare_builtin("__builtin_uaddl_overflow", &bool_ctype, 0, &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype, NULL); + declare_builtin("__builtin_uaddll_overflow", &bool_ctype, 0, &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype, NULL); + declare_builtin("__builtin_umul_overflow", &bool_ctype, 0, &uint_ctype, &uint_ctype, &uint_ptr_ctype, NULL); + declare_builtin("__builtin_umull_overflow", &bool_ctype, 0, &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype, NULL); + declare_builtin("__builtin_umulll_overflow", &bool_ctype, 0, &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype, NULL); + declare_builtin("__builtin_unreachable", &void_ctype, 0, NULL); + declare_builtin("__builtin_usub_overflow", &bool_ctype, 0, &uint_ctype, &uint_ctype, &uint_ptr_ctype, NULL); + declare_builtin("__builtin_usubl_overflow", &bool_ctype, 0, &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype, NULL); + declare_builtin("__builtin_usubll_overflow", &bool_ctype, 0, &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype, NULL); + declare_builtin("__builtin_va_arg_pack_len", size_t_ctype, 0, NULL); + declare_builtin("__builtin_vprintf", &int_ctype, 0, &const_string_ctype, va_list_ctype, NULL); + declare_builtin("__builtin_vsnprintf", &int_ctype, 0, &string_ctype, size_t_ctype, &const_string_ctype, va_list_ctype, NULL); + declare_builtin("__builtin_vsprintf", &int_ctype, 0, &string_ctype, &const_string_ctype, va_list_ctype, NULL); + + declare_builtin("__builtin___memcpy_chk", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype, NULL); + declare_builtin("__builtin___memmove_chk", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype, NULL); + declare_builtin("__builtin___mempcpy_chk", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype, NULL); + declare_builtin("__builtin___memset_chk", &ptr_ctype, 0, &ptr_ctype, &int_ctype, size_t_ctype, size_t_ctype, NULL); + declare_builtin("__builtin___snprintf_chk", &int_ctype, 1, &string_ctype, size_t_ctype, &int_ctype , size_t_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin___sprintf_chk", &int_ctype, 1, &string_ctype, &int_ctype, size_t_ctype, &const_string_ctype, NULL); + declare_builtin("__builtin___stpcpy_chk", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, NULL); + declare_builtin("__builtin___strcat_chk", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, NULL); + declare_builtin("__builtin___strcpy_chk", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, NULL); + declare_builtin("__builtin___strncat_chk", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, size_t_ctype, NULL); + declare_builtin("__builtin___strncpy_chk", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, size_t_ctype, NULL); + declare_builtin("__builtin___vsnprintf_chk", &int_ctype, 0, &string_ctype, size_t_ctype, &int_ctype, size_t_ctype, &const_string_ctype, va_list_ctype, NULL); + declare_builtin("__builtin___vsprintf_chk", &int_ctype, 0, &string_ctype, &int_ctype, size_t_ctype, &const_string_ctype, va_list_ctype, NULL); + + declare_builtin("__sync_add_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_and_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_bool_compare_and_swap", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_fetch_and_add", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_fetch_and_and", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_fetch_and_nand", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_fetch_and_or", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_fetch_and_sub", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_fetch_and_xor", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_lock_release", &void_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_lock_test_and_set", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_nand_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_or_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_sub_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_synchronize", &void_ctype, 0, NULL); + declare_builtin("__sync_val_compare_and_swap", &int_ctype, 1, &ptr_ctype, NULL); + declare_builtin("__sync_xor_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); + + // Blackfin-specific stuff + declare_builtin("__builtin_bfin_csync", &void_ctype, 0, NULL); + declare_builtin("__builtin_bfin_ssync", &void_ctype, 0, NULL); + declare_builtin("__builtin_bfin_norm_fr1x32", &int_ctype, 0, &int_ctype, NULL); } diff --git a/usr/src/tools/smatch/src/c2xml.c b/usr/src/tools/smatch/src/c2xml.c index c45d5818a9..1a24c3a812 100644 --- a/usr/src/tools/smatch/src/c2xml.c +++ b/usr/src/tools/smatch/src/c2xml.c @@ -318,12 +318,12 @@ int main(int argc, char **argv) */ symlist = sparse_initialize(argc, argv, &filelist); - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { examine_symbol_list(file, symlist); sparse_keep_tokens(file); examine_symbol_list(file, file_scope->symbols); examine_symbol_list(file, global_scope->symbols); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); xmlSaveFormatFileEnc("-", doc, "UTF-8", 1); diff --git a/usr/src/tools/smatch/src/cgcc b/usr/src/tools/smatch/src/cgcc index 554d1f2e71..43c4b1d2ec 100755 --- a/usr/src/tools/smatch/src/cgcc +++ b/usr/src/tools/smatch/src/cgcc @@ -1,6 +1,9 @@ #!/usr/bin/perl -w # ----------------------------------------------------------------------------- +use strict; +use warnings; + my $cc = $ENV{'REAL_CC'} || 'cc'; my $check = $ENV{'CHECK'} || 'sparse'; my $ccom = $cc; @@ -14,20 +17,42 @@ my $do_compile = 1; my $gcc_base_dir; my $multiarch_dir; my $verbose = 0; +my $nargs = 0; while (@ARGV) { $_ = shift(@ARGV); + + if ($nargs) { + $nargs--; + goto add_option; + } + # Look for a .c file. We don't want to run the checker on .o or .so files - # in the link run. (This simplistic check knows nothing about options - # with arguments, but it seems to do the job.) + # in the link run. $do_check = 1 if /^[^-].*\.c$/; # Ditto for stdin. $do_check = 1 if $_ eq '-'; + if (/^-(o|MF|MT|MQ)$/) { + # Need to be checked explicitly since otherwise + # the argument would be processed as a + # (non-existant) source file or as an option. + die ("$0: missing argument for $_") if !@ARGV; + $nargs = 1; + } + + # Ignore the extension if '-x c' is given. + if ($_ eq '-x') { + die ("$0: missing argument for $_") if !@ARGV; + die ("$0: invalid argument for $_") if $ARGV[0] ne 'c'; + $do_check = 1; + $nargs = 1; + } + $m32 = 1 if /^-m32$/; $m64 = 1 if /^-m64$/; - $gendeps = 1 if /^-M$/; + $gendeps = 1 if /^-(M|MM|MD|MMD)$/; if (/^-target=(.*)$/) { $check .= &add_specs ($1); @@ -57,6 +82,7 @@ while (@ARGV) { $verbose = 1 if $_ eq '-v'; +add_option: my $this_arg = ' ' . "e_arg ($_); $cc .= $this_arg unless &check_only_option ($_); $check .= $this_arg; @@ -101,9 +127,10 @@ exit 0; sub check_only_option { my ($arg) = @_; - return 1 if $arg =~ /^-W(no-?)?(address-space|bitwise|cast-to-as|cast-truncate|context|decl|default-bitfield-sign|designated-init|do-while|enum-mismatch|external-function-has-definition|init-cstring|memcpy-max-count|non-ansi-function-declaration|non-pointer-null|old-initializer|one-bit-signed-bitfield|override-init-all|paren-string|ptr-subtraction-blows|return-void|sizeof-bool|sparse-all|sparse-error|transparent-union|typesign|undef|unknown-attribute)$/; + return 1 if $arg =~ /^-W(no-?)?(address-space|bitwise|cast-to-as|cast-truncate|constant-suffix|context|decl|default-bitfield-sign|designated-init|do-while|enum-mismatch|external-function-has-definition|init-cstring|memcpy-max-count|non-pointer-null|old-initializer|one-bit-signed-bitfield|override-init-all|paren-string|ptr-subtraction-blows|return-void|sizeof-bool|sparse-all|sparse-error|transparent-union|typesign|undef|unknown-attribute)$/; return 1 if $arg =~ /^-v(no-?)?(entry|dead)$/; - return 1 if $arg =~ /^-f(dump-linearize|memcpy-max-count)(=\S*)?$/; + return 1 if $arg =~ /^-f(dump-ir|memcpy-max-count|diagnostic-prefix)(=\S*)?$/; + return 1 if $arg =~ /^-f(mem2reg|optim)(-enable|-disable|=last)?$/; return 0; } @@ -241,9 +268,18 @@ sub add_specs { } elsif ($spec eq 'openbsd') { return &add_specs ('unix') . ' -D__OpenBSD__=1'; + } elsif ($spec eq 'freebsd') { + return &add_specs ('unix') . + ' -D__FreeBSD__=1'; + } elsif ($spec eq 'netbsd') { + return &add_specs ('unix') . + ' -D__NetBSD__=1'; } elsif ($spec eq 'darwin') { return - ' -D__APPLE__=1 -D__MACH__=1'; + ' -D__APPLE__=1 -D__APPLE_CC__=1 -D__MACH__=1'; + } elsif ($spec eq 'gnu') { # Hurd + return &add_specs ('unix') . # So, GNU is Unix, uh? + ' -D__GNU__=1 -D__gnu_hurd__=1 -D__MACH__=1'; } elsif ($spec eq 'unix') { return ' -Dunix=1 -D__unix=1 -D__unix__=1'; } elsif ( $spec =~ /^cygwin/) { @@ -256,69 +292,88 @@ sub add_specs { " -D'_fastcall=__attribute__((__fastcall__))'" . " -D'__fastcall=__attribute__((__fastcall__))'" . " -D'__declspec(x)=__attribute__((x))'"; - } elsif ($spec eq 'i86') { - return (' -D__i386=1 -D__i386__=1' . - &integer_types (8, 16, 32, $m64 ? 64 : 32, 64) . - &float_types (1, 1, 21, [24,8], [53,11], [64,15]) . - &define_size_t ($m64 ? "long unsigned int" : "unsigned int") . - ' -D__SIZEOF_POINTER__=' . ($m64 ? '8' : '4')); + } elsif ($spec eq 'i386') { + return ( + &float_types (1, 1, 21, [24,8], [53,11], [64,15])); } elsif ($spec eq 'sparc') { - return (' -D__sparc=1 -D__sparc__=1' . + return ( &integer_types (8, 16, 32, $m64 ? 64 : 32, 64) . &float_types (1, 1, 33, [24,8], [53,11], [113,15]) . &define_size_t ($m64 ? "long unsigned int" : "unsigned int") . ' -D__SIZEOF_POINTER__=' . ($m64 ? '8' : '4')); } elsif ($spec eq 'sparc64') { - return (' -D__sparc=1 -D__sparc__=1 -D__sparcv9__=1 -D__sparc64__=1 -D__arch64__=1 -D__LP64__=1' . + return ( &integer_types (8, 16, 32, 64, 64, 128) . &float_types (1, 1, 33, [24,8], [53,11], [113,15]) . &define_size_t ("long unsigned int") . ' -D__SIZEOF_POINTER__=8'); } elsif ($spec eq 'x86_64') { - return (' -D__x86_64=1 -D__x86_64__=1' . ($m32 ? '' : ' -D__LP64__=1') . - &integer_types (8, 16, 32, $m32 ? 32 : 64, 64, 128) . - &float_types (1, 1, 33, [24,8], [53,11], [113,15]) . - &define_size_t ($m32 ? "unsigned int" : "long unsigned int") . - ' -D__SIZEOF_POINTER__=' . ($m32 ? '4' : '8')); + return &float_types (1, 1, 33, [24,8], [53,11], [113,15]); } elsif ($spec eq 'ppc') { - return (' -D__powerpc__=1 -D_BIG_ENDIAN -D_STRING_ARCH_unaligned=1' . + return (' -D_BIG_ENDIAN -D_STRING_ARCH_unaligned=1' . &integer_types (8, 16, 32, $m64 ? 64 : 32, 64) . &float_types (1, 1, 21, [24,8], [53,11], [113,15]) . &define_size_t ($m64 ? "long unsigned int" : "unsigned int") . ' -D__SIZEOF_POINTER__=' . ($m64 ? '8' : '4')); } elsif ($spec eq 'ppc64') { - return (' -D__powerpc__=1 -D__PPC__=1 -D_STRING_ARCH_unaligned=1' . - ' -D__powerpc64__=1 -D__PPC64__=1' . - ' -m64' . + return (' -D_STRING_ARCH_unaligned=1 -m64' . &float_types (1, 1, 21, [24,8], [53,11], [113,15])); + } elsif ($spec eq 'ppc64+be') { + return &add_specs ('ppc64') . ' -mbig-endian -D_CALL_ELF=1'; + } elsif ($spec eq 'ppc64+le') { + return &add_specs ('ppc64') . ' -mlittle-endian -D_CALL_ELF=2'; } elsif ($spec eq 's390x') { - return (' -D__s390x__ -D__s390__ -D_BIG_ENDIAN' . + return (' -D_BIG_ENDIAN' . &integer_types (8, 16, 32, $m64 ? 64 : 32, 64) . &float_types (1, 1, 36, [24,8], [53,11], [113,15]) . &define_size_t ("long unsigned int") . ' -D__SIZEOF_POINTER__=' . ($m64 ? '8' : '4')); } elsif ($spec eq 'arm') { - chomp (my $gccmachine = `$cc -dumpmachine`); - my $cppsymbols = ' -D__arm__=1 -m32'; - - if ($gccmachine eq 'arm-linux-gnueabihf') { - $cppsymbols .= ' -D__ARM_PCS_VFP=1'; - } - - return ($cppsymbols . + return (' -m32' . &float_types (1, 1, 36, [24,8], [53,11], [53, 11])); + } elsif ($spec eq 'arm+hf') { + return &add_specs ('arm') . ' -D__ARM_PCS_VFP=1'; } elsif ($spec eq 'aarch64') { - return (' -D__aarch64__=1 -m64' . + return (' -m64' . &float_types (1, 1, 36, [24,8], [53,11], [113,15])); } elsif ($spec eq 'host_os_specs') { my $os = `uname -s`; chomp $os; return &add_specs (lc $os); } elsif ($spec eq 'host_arch_specs') { - my $arch = `uname -m`; + my $gccmachine; + my $arch; + + $gccmachine = `$ccom -dumpmachine`; + chomp $gccmachine; + + if ($gccmachine =~ '^aarch64-') { + return &add_specs ('aarch64'); + } elsif ($gccmachine =~ '^arm-.*eabihf$') { + return &add_specs ('arm+hf'); + } elsif ($gccmachine =~ '^arm-') { + return &add_specs ('arm'); + } elsif ($gccmachine =~ '^i[23456]86-') { + return &add_specs ('i386'); + } elsif ($gccmachine =~ '^(powerpc|ppc)64le-') { + return &add_specs ('ppc64+le'); + } elsif ($gccmachine =~ '^s390x-') { + return &add_specs ('s390x'); + } elsif ($gccmachine eq 'x86_64-linux-gnux32') { + return &add_specs ('x86_64') . ' -mx32'; + } elsif ($gccmachine =~ '^x86_64-') { + return &add_specs ('x86_64'); + } + + # fall back to uname -m to determine the specifics. + # Note: this is only meaningful when using natively + # since information about the host is used to + # guess characteristics of the target. + + $arch = `uname -m`; chomp $arch; if ($arch =~ /^(i.?86|athlon)$/i) { - return &add_specs ('i86'); + return &add_specs ('i386'); } elsif ($arch =~ /^(sun4u)$/i) { return &add_specs ('sparc'); } elsif ($arch =~ /^(x86_64)$/i) { @@ -326,9 +381,9 @@ sub add_specs { } elsif ($arch =~ /^(ppc)$/i) { return &add_specs ('ppc'); } elsif ($arch =~ /^(ppc64)$/i) { - return &add_specs ('ppc64') . ' -mbig-endian -D_CALL_ELF=1'; + return &add_specs ('ppc64+be'); } elsif ($arch =~ /^(ppc64le)$/i) { - return &add_specs ('ppc64') . ' -mlittle-endian -D_CALL_ELF=2'; + return &add_specs ('ppc64+le'); } elsif ($arch =~ /^(s390x)$/i) { return &add_specs ('s390x'); } elsif ($arch =~ /^(sparc64)$/i) { diff --git a/usr/src/tools/smatch/src/char.c b/usr/src/tools/smatch/src/char.c index c52521bc83..f26b2a8066 100644 --- a/usr/src/tools/smatch/src/char.c +++ b/usr/src/tools/smatch/src/char.c @@ -84,7 +84,7 @@ void get_char_constant(struct token *token, unsigned long long *val) end = p + type - TOKEN_WIDE_CHAR; } p = parse_escape(p, &v, end, - type < TOKEN_WIDE_CHAR ? bits_in_char : bits_in_wchar, token->pos); + type < TOKEN_WIDE_CHAR ? bits_in_char : wchar_ctype->bit_size, token->pos); if (p != end) warning(token->pos, "multi-character character constant"); @@ -113,7 +113,7 @@ struct token *get_string_constant(struct token *token, struct expression *expr) done = next; } } - bits = is_wide ? bits_in_wchar : bits_in_char; + bits = is_wide ? wchar_ctype->bit_size: bits_in_char; while (token != done) { unsigned v; const char *p = token->string->data; diff --git a/usr/src/tools/smatch/src/check_access_ok_math.c b/usr/src/tools/smatch/src/check_access_ok_math.c index 7cfcdc4a7d..a6b746b9ed 100644 --- a/usr/src/tools/smatch/src/check_access_ok_math.c +++ b/usr/src/tools/smatch/src/check_access_ok_math.c @@ -76,24 +76,15 @@ static void match_access_ok(const char *fn, struct expression *expr, void *data) static void split_asm_constraints(struct expression_list *expr_list) { struct expression *expr; - int state = 0; int i; i = 0; FOR_EACH_PTR(expr_list, expr) { - - switch (state) { - case 0: /* identifier */ - case 1: /* constraint */ - state++; - continue; - case 2: /* expression */ - state = 0; - if (i == 1) - match_size(expr); - i++; - continue; - } + i++; + if (expr->type != EXPR_ASM_OPERAND) + continue; + if (i == 1) + match_size(expr->expr); } END_FOR_EACH_PTR(expr); } diff --git a/usr/src/tools/smatch/src/check_arm64_tagged.c b/usr/src/tools/smatch/src/check_arm64_tagged.c new file mode 100644 index 0000000000..e126552e59 --- /dev/null +++ b/usr/src/tools/smatch/src/check_arm64_tagged.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2019 ARM. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt + */ + +#include "smatch.h" +#include "smatch_extra.h" +#include "smatch_function_hashtable.h" + +static bool expr_has_memory_addr(struct expression *expr); + +static DEFINE_HASHTABLE_SEARCH(search_symbol, char, char); +static DEFINE_HASHTABLE_INSERT(insert_symbol, char, char); +static struct hashtable *symbols; + +static void match_assign(struct expression *expr) +{ + char *left_name; + struct symbol *left_sym; + + left_name = expr_to_var_sym(expr->left, &left_sym); + if (!left_name || !left_sym) + return; + + /* + * Once we have spotted a symbol of interest (one that may hold + * an untagged memory address), we keep track of any assignments + * made, such that we can also treat the assigned symbol as something + * of interest. This tracking is limited in scope to the function. + */ + if (expr_has_memory_addr(expr->right)) + insert_symbol(symbols, left_name, left_name); +} + +static void match_endfunc(struct symbol *sym) +{ + destroy_function_hashtable(symbols); + symbols = create_function_hashtable(4000); +} + +static bool expr_has_untagged_symbol(struct expression *expr) +{ + char *name; + struct symbol *sym; + + if (expr->type != EXPR_SYMBOL) + return false; + + name = expr_to_var_sym(expr, &sym); + if (!name || !sym) + return false; + + /* See if this is something we already know is of interest */ + if (search_symbol(symbols, name)) + return true; + + return false; +} + +static bool expr_has_untagged_member(struct expression *expr) +{ + if (expr->type != EXPR_DEREF) + return false; + + if (!strcmp(expr->member->name, "vm_start") || + !strcmp(expr->member->name, "vm_end") || + !strcmp(expr->member->name, "addr_limit")) + return true; + + return false; +} + +static bool expr_has_macro_with_name(struct expression *expr, const char *macro_name) +{ + char *name; + + name = get_macro_name(expr->pos); + return (name && !strcmp(name, macro_name)); +} + +static bool expr_has_untagged_macro(struct expression *expr) +{ + if (expr_has_macro_with_name(expr, "PAGE_SIZE") || + expr_has_macro_with_name(expr, "PAGE_MASK") || + expr_has_macro_with_name(expr, "TASK_SIZE")) + return true; + + /** + * We can't detect a marco (such as PAGE_MASK) inside another macro + * such as offset_in_page, therefore we have to detect the outer macro + * instead. + */ + if (expr_has_macro_with_name(expr, "offset_in_page")) + return true; + + return false; +} + +/* + * Identify expressions that contain memory addresses, in the future + * we may use annotations on symbols or function parameters. + */ +static bool expr_has_memory_addr(struct expression *expr) +{ + if (expr->type == EXPR_PREOP || expr->type == EXPR_POSTOP) + expr = strip_expr(expr->unop); + + if (expr_has_untagged_member(expr)) + return true; + + if (expr_has_untagged_macro(expr)) + return true; + + if (expr_has_untagged_symbol(expr)) + return true; + + return false; +} + +int rl_is_larger_or_equal(struct range_list *rl, sval_t sval) +{ + struct data_range *tmp; + + FOR_EACH_PTR(rl, tmp) { + if (sval_cmp(tmp->max, sval) >= 0) + return 1; + } END_FOR_EACH_PTR(tmp); + return 0; +} + +int rl_range_has_min_value(struct range_list *rl, sval_t sval) +{ + struct data_range *tmp; + + FOR_EACH_PTR(rl, tmp) { + if (!sval_cmp(tmp->min, sval)) { + return 1; + } + } END_FOR_EACH_PTR(tmp); + return 0; +} + +static bool rl_is_tagged(struct range_list *rl) +{ + sval_t invalid = { .type = &ullong_ctype, .value = (1ULL << 56) }; + sval_t invalid_kernel = { .type = &ullong_ctype, .value = (0xff8ULL << 52) }; + + /* + * We only care for tagged addresses, thus ignore anything where the + * ranges of potential values cannot possibly have any of the top byte + * bits set. + */ + if (!rl_is_larger_or_equal(rl, invalid)) + return false; + + /* + * Tagged addresses are untagged in the kernel by using sign_extend64 in + * the untagged_addr macro. For userspace addresses bit 55 will always + * be 0 and thus this has the effect of clearing the top byte. However + * for kernel addresses this is not true and the top bits end up set to + * all 1s. The untagged_addr macro results in leaving a gap in the range + * of possible values which can exist, thus let's look for a tell-tale + * range which starts from (0xff8ULL << 52). + */ + if (rl_range_has_min_value(rl, invalid_kernel)) + return false; + + return true; +} + +static void match_condition(struct expression *expr) +{ + struct range_list *rl = NULL; + struct expression *val = NULL; + struct symbol *type; + char *var_name; + + /* + * Match instances where something is compared against something + * else - we include binary operators as these are commonly used + * to make a comparison, e.g. if (start & ~PAGE_MASK). + */ + if (expr->type != EXPR_COMPARE && + expr->type != EXPR_BINOP) + return; + + /* + * Look on both sides of the comparison for something that shouldn't + * be compared with a tagged address, e.g. macros such as PAGE_MASK + * or struct members named .vm_start. + */ + if (expr_has_memory_addr(expr->left)) + val = expr->right; + + /* + * The macro 'offset_in_page' has the PAGE_MASK macro inside it, this + * results in 'expr_has_memory_addr' returning true for both sides. To + * work around this we assume PAGE_MASK (or similar) is on the right + * side, thus we do the following test last. + */ + if (expr_has_memory_addr(expr->right)) + val = expr->left; + + if (!val) + return; + + /* We only care about memory addresses which are 64 bits */ + type = get_type(val); + if (!type || type_bits(type) != 64) + return; + + /* We only care for comparison against user originated data */ + if (!get_user_rl(val, &rl)) + return; + + /* We only care for tagged addresses */ + if (!rl_is_tagged(rl)) + return; + + /* Finally, we believe we may have spotted a risky comparison */ + var_name = expr_to_var(val); + if (var_name) + sm_warning("comparison of a potentially tagged address (%s, %d, %s)", get_function(), get_param_num(val), var_name); +} + +void check_arm64_tagged(int id) +{ + char *arch; + + if (option_project != PROJ_KERNEL) + return; + + /* Limit to aarch64 */ + arch = getenv("ARCH"); + if (!arch || strcmp(arch, "arm64")) + return; + + symbols = create_function_hashtable(4000); + + add_hook(&match_assign, ASSIGNMENT_HOOK); + add_hook(&match_condition, CONDITION_HOOK); + add_hook(&match_endfunc, END_FUNC_HOOK); +} diff --git a/usr/src/tools/smatch/src/check_check_deref.c b/usr/src/tools/smatch/src/check_check_deref.c index da2a5c942b..6eb1300add 100644 --- a/usr/src/tools/smatch/src/check_check_deref.c +++ b/usr/src/tools/smatch/src/check_check_deref.c @@ -135,8 +135,11 @@ free: static void match_condition(struct expression *expr) { struct smatch_state *true_state = NULL; + char *name; - if (get_macro_name(expr->pos)) + name = get_macro_name(expr->pos); + if (name && + (strcmp(name, "likely") != 0 && strcmp(name, "unlikely") != 0)) return; if (!is_pointer(expr)) diff --git a/usr/src/tools/smatch/src/check_continue_vs_break.c b/usr/src/tools/smatch/src/check_continue_vs_break.c index c22ce0ed65..d12577e0e6 100644 --- a/usr/src/tools/smatch/src/check_continue_vs_break.c +++ b/usr/src/tools/smatch/src/check_continue_vs_break.c @@ -36,7 +36,7 @@ static int is_do_while_zero(struct statement *stmt) { if (!stmt->iterator_post_condition) return 0; - if (!is_zero(stmt->iterator_post_condition)) + if (!expr_is_zero(stmt->iterator_post_condition)) return 0; return 1; } diff --git a/usr/src/tools/smatch/src/check_debug.c b/usr/src/tools/smatch/src/check_debug.c index 155e62535c..8e328e8c1b 100644 --- a/usr/src/tools/smatch/src/check_debug.c +++ b/usr/src/tools/smatch/src/check_debug.c @@ -22,6 +22,7 @@ void show_sname_alloc(void); void show_data_range_alloc(void); void show_ptrlist_alloc(void); +void show_rl_ptrlist_alloc(void); void show_sm_state_alloc(void); int local_debug; @@ -204,13 +205,16 @@ static void match_user_rl(const char *fn, struct expression *expr, void *info) { struct expression *arg; struct range_list *rl = NULL; + bool capped = false; char *name; arg = get_argument_from_call_expr(expr->args, 0); name = expr_to_str(arg); get_user_rl(arg, &rl); - sm_msg("user rl: '%s' = '%s'", name, show_rl(rl)); + if (rl) + capped = user_rl_capped(arg); + sm_msg("user rl: '%s' = '%s'%s", name, show_rl(rl), capped ? " (capped)" : ""); free_string(name); } @@ -687,6 +691,8 @@ static void match_state_count(const char *fn, struct expression *expr, void *inf static void match_mem(const char *fn, struct expression *expr, void *info) { show_sname_alloc(); + show_data_range_alloc(); + show_rl_ptrlist_alloc(); show_ptrlist_alloc(); sm_msg("%lu pools", get_pool_count()); sm_msg("%d strees", unfree_stree); diff --git a/usr/src/tools/smatch/src/check_deref.c b/usr/src/tools/smatch/src/check_deref.c index d8777db233..2527d5de94 100644 --- a/usr/src/tools/smatch/src/check_deref.c +++ b/usr/src/tools/smatch/src/check_deref.c @@ -186,7 +186,7 @@ static void match_assign(struct expression *expr) { struct statement *stmt; - if (!is_zero(expr->right)) + if (!expr_is_zero(expr->right)) return; if (__in_fake_assign) diff --git a/usr/src/tools/smatch/src/check_deref_check.c b/usr/src/tools/smatch/src/check_deref_check.c index a06cc8bdc0..e0033c43a5 100644 --- a/usr/src/tools/smatch/src/check_deref_check.c +++ b/usr/src/tools/smatch/src/check_deref_check.c @@ -66,11 +66,14 @@ free: static void match_condition(struct expression *expr) { struct sm_state *sm; + char *name; if (__in_pre_condition) return; - if (get_macro_name(expr->pos)) + name = get_macro_name(expr->pos); + if (name && + (strcmp(name, "likely") != 0 && strcmp(name, "unlikely") != 0)) return; if (!is_pointer(expr)) diff --git a/usr/src/tools/smatch/src/check_dereferences_param.c b/usr/src/tools/smatch/src/check_dereferences_param.c index 25192491a3..3572be9238 100644 --- a/usr/src/tools/smatch/src/check_dereferences_param.c +++ b/usr/src/tools/smatch/src/check_dereferences_param.c @@ -78,8 +78,6 @@ static void match_dereference(struct expression *expr) { if (expr->type != EXPR_PREOP) return; - if (getting_address()) - return; check_deref(expr->unop); } diff --git a/usr/src/tools/smatch/src/check_double_checking.c b/usr/src/tools/smatch/src/check_double_checking.c index 83d10fd1c2..e1dc805308 100644 --- a/usr/src/tools/smatch/src/check_double_checking.c +++ b/usr/src/tools/smatch/src/check_double_checking.c @@ -47,9 +47,9 @@ static struct expression *strip_condition(struct expression *expr) if (expr->type == EXPR_COMPARE && (expr->op == SPECIAL_EQUAL || expr->op == SPECIAL_NOTEQUAL)) { - if (is_zero(expr->left)) + if (expr_is_zero(expr->left)) return strip_condition(expr->right); - if (is_zero(expr->right)) + if (expr_is_zero(expr->right)) return strip_condition(expr->left); } @@ -131,6 +131,9 @@ static int previous_statement_was_synchronize(void) struct position prev_pos; char *ident; + if (!__cur_stmt) + return 0; + if (__prev_stmt) { prev_pos = __prev_stmt->pos; prev_pos.line -= 3; diff --git a/usr/src/tools/smatch/src/check_free.c b/usr/src/tools/smatch/src/check_free.c index 44df17e0d3..7a10f7f505 100644 --- a/usr/src/tools/smatch/src/check_free.c +++ b/usr/src/tools/smatch/src/check_free.c @@ -36,10 +36,10 @@ static void ok_to_use(struct sm_state *sm, struct expression *mod_expr) set_state(my_id, sm->name, sm->sym, &ok); } -static void pre_merge_hook(struct sm_state *sm) +static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) { if (is_impossible_path()) - set_state(my_id, sm->name, sm->sym, &ok); + set_state(my_id, cur->name, cur->sym, &ok); } static int is_freed(struct expression *expr) diff --git a/usr/src/tools/smatch/src/check_free_strict.c b/usr/src/tools/smatch/src/check_free_strict.c index 4da29cced5..a8c224f10d 100644 --- a/usr/src/tools/smatch/src/check_free_strict.c +++ b/usr/src/tools/smatch/src/check_free_strict.c @@ -36,10 +36,27 @@ static void ok_to_use(struct sm_state *sm, struct expression *mod_expr) set_state(my_id, sm->name, sm->sym, &ok); } -static void pre_merge_hook(struct sm_state *sm) +static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) { if (is_impossible_path()) - set_state(my_id, sm->name, sm->sym, &ok); + set_state(my_id, cur->name, cur->sym, &ok); +} + +static struct smatch_state *unmatched_state(struct sm_state *sm) +{ + struct smatch_state *state; + sval_t sval; + + if (sm->state != &freed) + return &undefined; + + state = get_state(SMATCH_EXTRA, sm->name, sm->sym); + if (!state) + return &undefined; + if (!estate_get_single_value(state, &sval) || sval.value != 0) + return &undefined; + /* It makes it easier to consider NULL pointers as freed. */ + return &freed; } static int is_freed(struct expression *expr) @@ -341,6 +358,7 @@ void check_free_strict(int id) add_modification_hook_late(my_id, &ok_to_use); add_pre_merge_hook(my_id, &pre_merge_hook); + add_unmatched_state_hook(my_id, &unmatched_state); select_return_states_hook(PARAM_FREED, &set_param_freed); add_untracked_param_hook(&match_untracked); diff --git a/usr/src/tools/smatch/src/check_get_user_overflow.c b/usr/src/tools/smatch/src/check_get_user_overflow.c index 20ec952c88..395019f2e3 100644 --- a/usr/src/tools/smatch/src/check_get_user_overflow.c +++ b/usr/src/tools/smatch/src/check_get_user_overflow.c @@ -108,7 +108,7 @@ static void match_assign(struct expression *expr) return; } name = expr_to_var(expr->right); - if (!name || strcmp(name, "__val_gu") != 0) + if (!name || (strcmp(name, "__val_gu") != 0 && strcmp(name, "__gu_val"))) goto free; set_state_expr(my_max_id, expr->left, &user_data); set_state_expr(my_min_id, expr->left, &user_data); @@ -127,14 +127,15 @@ static void check_expr(struct expression *expr) sm = get_sm_state_expr(my_max_id, expr); if (sm && slist_has_state(sm->possible, &user_data)) { - if (!get_absolute_max(expr, &max) || sval_cmp_val(max, 20000) > 0) + get_absolute_max(expr, &max); + if (sval_cmp_val(max, 20000) > 0) overflow = 1; } sm = get_sm_state_expr(my_min_id, expr); if (sm && slist_has_state(sm->possible, &user_data)) { - if (!get_absolute_min(expr, &sval) || - (sval_is_negative(sval) && sval_cmp_val(sval, -20000) < 0)) + get_absolute_min(expr, &sval); + if (sval_is_negative(sval) && sval_cmp_val(sval, -20000) < 0) underflow = 1; } diff --git a/usr/src/tools/smatch/src/check_kernel.c b/usr/src/tools/smatch/src/check_kernel.c index 69c9a37555..a67511ef43 100644 --- a/usr/src/tools/smatch/src/check_kernel.c +++ b/usr/src/tools/smatch/src/check_kernel.c @@ -104,14 +104,18 @@ static void match_param_err_or_null(const char *fn, struct expression *call_expr { int param = PTR_INT(_param); struct expression *arg; - struct range_list *rl; + struct range_list *pre, *rl; struct smatch_state *pre_state; struct smatch_state *end_state; arg = get_argument_from_call_expr(call_expr->args, param); pre_state = get_state_expr(SMATCH_EXTRA, arg); + if (pre_state) + pre = estate_rl(pre_state); + else + pre = alloc_whole_rl(&ptr_ctype); call_results_to_rl(call_expr, &ptr_ctype, "0,(-4095)-(-1)", &rl); - rl = rl_intersection(estate_rl(pre_state), rl); + rl = rl_intersection(pre, rl); rl = cast_rl(get_type(arg), rl); end_state = alloc_estate_rl(rl); set_extra_expr_nomod(arg, end_state); diff --git a/usr/src/tools/smatch/src/check_list.h b/usr/src/tools/smatch/src/check_list.h index 6e0a0d7a8c..4a9c069a0e 100644 --- a/usr/src/tools/smatch/src/check_list.h +++ b/usr/src/tools/smatch/src/check_list.h @@ -40,7 +40,6 @@ CK(register_comparison) CK(register_comparison_links) CK(register_comparison_inc_dec) CK(register_comparison_inc_dec_links) -CK(register_local_values) CK(register_function_ptrs) CK(register_annotate) CK(register_start_states) @@ -48,7 +47,6 @@ CK(register_type_val) CK(register_data_source) CK(register_common_functions) CK(register_function_info) -CK(register_auto_copy) CK(register_type_links) CK(register_impossible) CK(register_impossible_return) @@ -178,6 +176,7 @@ CK(check_held_dev) CK(check_return_negative_var) CK(check_rosenberg) CK(check_rosenberg2) +CK(check_rosenberg3) CK(check_wait_for_common) CK(check_bogus_irqrestore) CK(check_zero_to_err_ptr) @@ -197,6 +196,8 @@ CK(check_implicit_dependencies) CK(check_wine_filehandles) CK(check_wine_WtoA) +CK(check_arm64_tagged) + /* illumos specific */ CK(check_all_func_returns) CK(check_cmn_err) diff --git a/usr/src/tools/smatch/src/check_locking.c b/usr/src/tools/smatch/src/check_locking.c index 81fe9dda75..f0b2ed4d46 100644 --- a/usr/src/tools/smatch/src/check_locking.c +++ b/usr/src/tools/smatch/src/check_locking.c @@ -456,10 +456,10 @@ static struct smatch_state *unmatched_state(struct sm_state *sm) return &start_state; } -static void pre_merge_hook(struct sm_state *sm) +static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) { if (is_impossible_path()) - set_state(my_id, sm->name, sm->sym, &impossible); + set_state(my_id, cur->name, cur->sym, &impossible); } static bool nestable(const char *name) diff --git a/usr/src/tools/smatch/src/check_memory.c b/usr/src/tools/smatch/src/check_memory.c deleted file mode 100644 index 4e27497581..0000000000 --- a/usr/src/tools/smatch/src/check_memory.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (C) 2008 Dan Carpenter. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt - */ - -#include <fcntl.h> -#include <unistd.h> -#include "parse.h" -#include "smatch.h" -#include "smatch_slist.h" - -static void check_sm_is_leaked(struct sm_state *sm); - -static int my_id; - -STATE(allocated); -STATE(assigned); -STATE(isfree); -STATE(malloced); -STATE(isnull); -STATE(unfree); - -/* - malloced --> allocated --> assigned --> isfree + - \-> isnull. \-> isfree + - - isfree --> unfree. - \-> isnull. -*/ - -static struct tracker_list *arguments; - -static const char *allocation_funcs[] = { - "malloc", - "kmalloc", - "kzalloc", - NULL, -}; - -static char *get_parent_name(struct symbol *sym) -{ - static char buf[256]; - - if (!sym || !sym->ident) - return NULL; - - snprintf(buf, 255, "-%s", sym->ident->name); - buf[255] = '\0'; - return alloc_string(buf); -} - -static int is_parent_sym(const char *name) -{ - if (!strncmp(name, "-", 1)) - return 1; - return 0; -} - -static int is_complex(struct expression *expr) -{ - char *name; - int ret = 1; - - name = expr_to_var(expr); - if (name) - ret = 0; - free_string(name); - return ret; -} - -static struct smatch_state *unmatched_state(struct sm_state *sm) -{ - if (is_parent_sym(sm->name)) - return &assigned; - return &undefined; -} - -static void assign_parent(struct symbol *sym) -{ - char *name; - - name = get_parent_name(sym); - if (!name) - return; - set_state(my_id, name, sym, &assigned); - free_string(name); -} - -static int parent_is_assigned(struct symbol *sym) -{ - struct smatch_state *state; - char *name; - - name = get_parent_name(sym); - if (!name) - return 0; - state = get_state(my_id, name, sym); - free_string(name); - if (state == &assigned) - return 1; - return 0; -} - -static int is_allocation(struct expression *expr) -{ - char *fn_name; - int i; - - if (expr->type != EXPR_CALL) - return 0; - - if (!(fn_name = expr_to_var_sym(expr->fn, NULL))) - return 0; - - for (i = 0; allocation_funcs[i]; i++) { - if (!strcmp(fn_name, allocation_funcs[i])) { - free_string(fn_name); - return 1; - } - } - free_string(fn_name); - return 0; -} - -static int is_freed(const char *name, struct symbol *sym) -{ - struct state_list *slist; - - slist = get_possible_states(my_id, name, sym); - if (slist_has_state(slist, &isfree)) { - return 1; - } - return 0; -} - -static int is_argument(struct symbol *sym) -{ - struct tracker *arg; - - FOR_EACH_PTR(arguments, arg) { - if (arg->sym == sym) - return 1; - } END_FOR_EACH_PTR(arg); - return 0; -} - -static void match_function_def(struct symbol *sym) -{ - struct symbol *arg; - - FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) { - add_tracker(&arguments, my_id, (arg->ident?arg->ident->name:"NULL"), arg); - } END_FOR_EACH_PTR(arg); -} - -static int is_parent(struct expression *expr) -{ - if (expr->type == EXPR_DEREF) - return 0; - return 1; -} - -static void match_assign(struct expression *expr) -{ - struct expression *left, *right; - char *left_name = NULL; - char *right_name = NULL; - struct symbol *left_sym, *right_sym; - struct smatch_state *state; - struct state_list *slist; - struct sm_state *tmp; - - left = strip_expr(expr->left); - left_name = expr_to_str_sym(left, &left_sym); - - right = strip_expr(expr->right); - while (right->type == EXPR_ASSIGNMENT) - right = right->left; - - if (left_name && left_sym && is_allocation(right) && - !(left_sym->ctype.modifiers & - (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE)) && - !parent_is_assigned(left_sym)) { - set_state(my_id, left_name, left_sym, &malloced); - goto exit; - } - - right_name = expr_to_str_sym(right, &right_sym); - - if (right_name && (state = get_state(my_id, right_name, right_sym))) { - if (state == &isfree && !is_complex(right)) - sm_error("assigning freed pointer '%s'", right_name); - set_state(my_id, right_name, right_sym, &assigned); - } - - if (is_zero(expr->right)) { - slist = get_possible_states(my_id, left_name, left_sym); - - FOR_EACH_PTR(slist, tmp) { - check_sm_is_leaked(tmp); - } END_FOR_EACH_PTR(tmp); - } - - if (is_freed(left_name, left_sym)) { - set_state(my_id, left_name, left_sym, &unfree); - } - if (left_name && is_parent(left)) - assign_parent(left_sym); - if (right_name && is_parent(right)) - assign_parent(right_sym); -exit: - free_string(left_name); - free_string(right_name); -} - -static int is_null(const char *name, struct symbol *sym) -{ - struct smatch_state *state; - - state = get_state(my_id, name, sym); - if (state && !strcmp(state->name, "isnull")) - return 1; - return 0; -} - -static void set_unfree(struct sm_state *sm, struct expression *mod_expr) -{ - if (slist_has_state(sm->possible, &isfree)) - set_state(my_id, sm->name, sm->sym, &unfree); -} - -static void match_free_func(const char *fn, struct expression *expr, void *data) -{ - struct expression *ptr_expr; - char *ptr_name; - struct symbol *ptr_sym; - int arg_num = PTR_INT(data); - - ptr_expr = get_argument_from_call_expr(expr->args, arg_num); - ptr_name = expr_to_var_sym(ptr_expr, &ptr_sym); - if (!ptr_name) - return; - set_state(my_id, ptr_name, ptr_sym, &isfree); - free_string(ptr_name); -} - -static int possibly_allocated(struct state_list *slist) -{ - struct sm_state *tmp; - - FOR_EACH_PTR(slist, tmp) { - if (tmp->state == &allocated) - return 1; - if (tmp->state == &malloced) - return 1; - } END_FOR_EACH_PTR(tmp); - return 0; -} - -static void check_sm_is_leaked(struct sm_state *sm) -{ - if (possibly_allocated(sm->possible) && - !is_null(sm->name, sm->sym) && - !is_argument(sm->sym) && - !parent_is_assigned(sm->sym)) - sm_error("memory leak of '%s'", sm->name); -} - -static void check_tracker_is_leaked(struct tracker *t) -{ - struct sm_state *sm; - - sm = get_sm_state(t->owner, t->name, t->sym); - if (sm) - check_sm_is_leaked(sm); - __free_tracker(t); -} - -static void match_declarations(struct symbol *sym) -{ - const char *name; - - if ((get_base_type(sym))->type == SYM_ARRAY) { - return; - } - - name = sym->ident->name; - - if (sym->initializer) { - if (is_allocation(sym->initializer)) { - set_state(my_id, name, sym, &malloced); - add_scope_hook((scope_hook *)&check_tracker_is_leaked, - alloc_tracker(my_id, name, sym)); - scoped_state(my_id, name, sym); - } else { - assign_parent(sym); - } - } -} - -static void check_for_allocated(void) -{ - struct stree *stree; - struct sm_state *tmp; - - stree = __get_cur_stree(); - FOR_EACH_MY_SM(my_id, stree, tmp) { - check_sm_is_leaked(tmp); - } END_FOR_EACH_SM(tmp); -} - -static void match_return(struct expression *ret_value) -{ - char *name; - struct symbol *sym; - - if (__inline_fn) - return; - name = expr_to_str_sym(ret_value, &sym); - if (sym) - assign_parent(sym); - free_string(name); - check_for_allocated(); -} - -static void set_new_true_false_paths(const char *name, struct symbol *sym) -{ - struct smatch_state *tmp; - - tmp = get_state(my_id, name, sym); - - if (!tmp) { - return; - } - - if (tmp == &isfree) { - sm_warning("why do you care about freed memory? '%s'", name); - } - - if (tmp == &assigned) { - /* we don't care about assigned pointers any more */ - return; - } - set_true_false_states(my_id, name, sym, &allocated, &isnull); -} - -static void match_condition(struct expression *expr) -{ - struct symbol *sym; - char *name; - - expr = strip_expr(expr); - switch (expr->type) { - case EXPR_PREOP: - case EXPR_SYMBOL: - case EXPR_DEREF: - name = expr_to_var_sym(expr, &sym); - if (!name) - return; - set_new_true_false_paths(name, sym); - free_string(name); - return; - case EXPR_ASSIGNMENT: - /* You have to deal with stuff like if (a = b = c) */ - match_condition(expr->right); - match_condition(expr->left); - return; - default: - return; - } -} - -static void match_function_call(struct expression *expr) -{ - struct expression *tmp; - struct symbol *sym; - char *name; - struct sm_state *state; - - FOR_EACH_PTR(expr->args, tmp) { - tmp = strip_expr(tmp); - name = expr_to_str_sym(tmp, &sym); - if (!name) - continue; - if ((state = get_sm_state(my_id, name, sym))) { - if (possibly_allocated(state->possible)) { - set_state(my_id, name, sym, &assigned); - } - } - assign_parent(sym); - free_string(name); - } END_FOR_EACH_PTR(tmp); -} - -static void match_end_func(struct symbol *sym) -{ - if (__inline_fn) - return; - check_for_allocated(); -} - -static void match_after_func(struct symbol *sym) -{ - if (__inline_fn) - return; - free_trackers_and_list(&arguments); -} - -static void register_funcs_from_file(void) -{ - struct token *token; - const char *func; - int arg; - - token = get_tokens_file("kernel.frees_argument"); - if (!token) - return; - if (token_type(token) != TOKEN_STREAMBEGIN) - return; - token = token->next; - while (token_type(token) != TOKEN_STREAMEND) { - if (token_type(token) != TOKEN_IDENT) - return; - func = show_ident(token->ident); - token = token->next; - if (token_type(token) != TOKEN_NUMBER) - return; - arg = atoi(token->number); - add_function_hook(func, &match_free_func, INT_PTR(arg)); - token = token->next; - } - clear_token_alloc(); -} - -void check_memory(int id) -{ - my_id = id; - add_unmatched_state_hook(my_id, &unmatched_state); - add_hook(&match_function_def, FUNC_DEF_HOOK); - add_hook(&match_declarations, DECLARATION_HOOK); - add_hook(&match_function_call, FUNCTION_CALL_HOOK); - add_hook(&match_condition, CONDITION_HOOK); - add_hook(&match_assign, ASSIGNMENT_HOOK); - add_hook(&match_return, RETURN_HOOK); - add_hook(&match_end_func, END_FUNC_HOOK); - add_hook(&match_after_func, AFTER_FUNC_HOOK); - add_modification_hook(my_id, &set_unfree); - if (option_project == PROJ_KERNEL) { - add_function_hook("kfree", &match_free_func, (void *)0); - register_funcs_from_file(); - } else { - add_function_hook("free", &match_free_func, (void *)0); - } -} diff --git a/usr/src/tools/smatch/src/check_memset.c b/usr/src/tools/smatch/src/check_memset.c index 0213693afa..785e63742d 100644 --- a/usr/src/tools/smatch/src/check_memset.c +++ b/usr/src/tools/smatch/src/check_memset.c @@ -19,22 +19,39 @@ static int my_id; -static void match_memset(const char *fn, struct expression *expr, void *data) +static void check_size_not_zero(struct expression *expr) { - struct expression *arg_expr; sval_t sval; - arg_expr = get_argument_from_call_expr(expr->args, 2); - - if (arg_expr->type != EXPR_VALUE) + if (expr->type != EXPR_VALUE) return; - if (!get_value(arg_expr, &sval)) + if (!get_value(expr, &sval)) return; if (sval.value != 0) return; sm_error("calling memset(x, y, 0);"); } +static void check_size_not_ARRAY_SIZE(struct expression *expr) +{ + char *name; + + name = get_macro_name(expr->pos); + if (name && strcmp(name, "ARRAY_SIZE") == 0) + sm_warning("calling memset(x, y, ARRAY_SIZE());"); +} + +static void match_memset(const char *fn, struct expression *expr, void *data) +{ + struct expression *arg_expr; + + arg_expr = get_argument_from_call_expr(expr->args, 2); + if (!arg_expr) + return; + check_size_not_zero(arg_expr); + check_size_not_ARRAY_SIZE(arg_expr); +} + void check_memset(int id) { my_id = id; diff --git a/usr/src/tools/smatch/src/check_nospec.c b/usr/src/tools/smatch/src/check_nospec.c index a2aea00dfe..f43f00af24 100644 --- a/usr/src/tools/smatch/src/check_nospec.c +++ b/usr/src/tools/smatch/src/check_nospec.c @@ -179,6 +179,8 @@ static int is_nospec_asm(struct statement *stmt) if (!stmt || stmt->type != STMT_ASM) return 0; + if (!stmt->asm_string) + return 0; macro = get_macro_name(stmt->asm_string->pos); if (!macro || strcmp(macro, "CALL_NOSPEC") != 0) return 0; diff --git a/usr/src/tools/smatch/src/check_readl_infinite_loops.c b/usr/src/tools/smatch/src/check_readl_infinite_loops.c index 8f3ca92ec5..48ea3d701c 100644 --- a/usr/src/tools/smatch/src/check_readl_infinite_loops.c +++ b/usr/src/tools/smatch/src/check_readl_infinite_loops.c @@ -126,7 +126,7 @@ static void before_loop(struct statement *stmt) if (!stmt || stmt->type != STMT_ITERATOR) return; - if (ptr_list_empty(state_at_start)) + if (ptr_list_empty((struct ptr_list *)state_at_start)) returned = 0; state = get_state(my_id, "depends on", NULL); push_state_at_start(state); diff --git a/usr/src/tools/smatch/src/check_rosenberg.c b/usr/src/tools/smatch/src/check_rosenberg.c index 7a03ee488d..97346cfafd 100644 --- a/usr/src/tools/smatch/src/check_rosenberg.c +++ b/usr/src/tools/smatch/src/check_rosenberg.c @@ -28,6 +28,7 @@ static int my_whole_id; static int my_member_id; +static int skb_put_id; STATE(cleared); @@ -124,19 +125,6 @@ static int has_global_scope(struct expression *expr) return toplevel(sym->scope); } -static int was_initialized(struct expression *expr) -{ - struct symbol *sym; - char *name; - - name = expr_to_var_sym(expr, &sym); - if (!name) - return 0; - if (sym->initializer) - return 1; - return 0; -} - static void match_clear(const char *fn, struct expression *expr, void *_arg_no) { struct expression *ptr; @@ -258,8 +246,21 @@ static void check_was_initialized(struct expression *data) if (has_global_scope(data)) return; - if (was_initialized(data)) + if (was_memset(data)) return; + if (warn_on_holey_struct(data)) + return; + check_members_initialized(data); +} + +static void check_skb_put(struct expression *data) +{ + data = strip_expr(data); + if (!data) + return; + if (data->type == EXPR_PREOP && data->op == '&') + data = strip_expr(data->unop); + if (was_memset(data)) return; if (warn_on_holey_struct(data)) @@ -291,14 +292,49 @@ static void db_param_cleared(struct expression *expr, int param, char *key, char match_clear(NULL, expr, INT_PTR(param)); } -static void match_assign(struct expression *expr) +static struct smatch_state *alloc_expr_state(struct expression *expr) +{ + struct smatch_state *state; + char *name; + + name = expr_to_str(expr); + if (!name) + return NULL; + + state = __alloc_smatch_state(0); + expr = strip_expr(expr); + state->name = alloc_sname(name); + free_string(name); + state->data = expr; + return state; +} + +static void match_skb_put(const char *fn, struct expression *expr, void *unused) { struct symbol *type; + struct smatch_state *state; type = get_type(expr->left); + type = get_real_base_type(type); if (!type || type->type != SYM_STRUCT) return; - set_state_expr(my_whole_id, expr->left, &cleared); + state = alloc_expr_state(expr->left); + set_state_expr(skb_put_id, expr->left, state); +} + +static void match_return_skb_put(struct expression *expr) +{ + struct sm_state *sm; + struct stree *stree; + + if (is_error_return(expr)) + return; + + stree = __get_cur_stree(); + + FOR_EACH_MY_SM(skb_put_id, stree, sm) { + check_skb_put(sm->state->data); + } END_FOR_EACH_SM(sm); } static void register_clears_argument(void) @@ -369,7 +405,6 @@ void check_rosenberg(int id) add_function_hook("__builtin_memset", &match_clear, INT_PTR(0)); add_function_hook("__builtin_memcpy", &match_clear, INT_PTR(0)); - add_hook(&match_assign, ASSIGNMENT_HOOK); register_clears_argument(); select_return_states_hook(PARAM_CLEARED, &db_param_cleared); @@ -386,3 +421,14 @@ void check_rosenberg2(int id) add_extra_mod_hook(&extra_mod_hook); } +void check_rosenberg3(int id) +{ + if (option_project != PROJ_KERNEL) + return; + + skb_put_id = id; + set_dynamic_states(skb_put_id); + add_function_assign_hook("skb_put", &match_skb_put, NULL); + add_hook(&match_return_skb_put, RETURN_HOOK); +} + diff --git a/usr/src/tools/smatch/src/check_testing_index_after_use.c b/usr/src/tools/smatch/src/check_testing_index_after_use.c index b04ceece5d..b40473d292 100644 --- a/usr/src/tools/smatch/src/check_testing_index_after_use.c +++ b/usr/src/tools/smatch/src/check_testing_index_after_use.c @@ -58,7 +58,7 @@ static void array_check(struct expression *expr) if (buf_comparison_index_ok(expr)) return; - if (getting_address()) + if (getting_address(expr)) return; if (is_capped(offset)) return; diff --git a/usr/src/tools/smatch/src/check_uninitialized.c b/usr/src/tools/smatch/src/check_uninitialized.c index 97d8d51f54..c3d236c8fc 100644 --- a/usr/src/tools/smatch/src/check_uninitialized.c +++ b/usr/src/tools/smatch/src/check_uninitialized.c @@ -24,10 +24,10 @@ static int my_id; STATE(uninitialized); STATE(initialized); -static void pre_merge_hook(struct sm_state *sm) +static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) { if (is_impossible_path()) - set_state(my_id, sm->name, sm->sym, &initialized); + set_state(my_id, cur->name, cur->sym, &initialized); } static void mark_members_uninitialized(struct symbol *sym) @@ -113,7 +113,7 @@ static void match_negative_comparison(struct expression *expr) if (expr->type != EXPR_COMPARE || expr->op != '<') return; - if (!is_zero(expr->right)) + if (!expr_is_zero(expr->right)) return; if (get_implied_max(expr->left, &max) && max.value == 0) return; diff --git a/usr/src/tools/smatch/src/check_unwind.c b/usr/src/tools/smatch/src/check_unwind.c index b29b8362f6..9b3e92e258 100644 --- a/usr/src/tools/smatch/src/check_unwind.c +++ b/usr/src/tools/smatch/src/check_unwind.c @@ -125,7 +125,7 @@ static int func_returns_int(void) if (!type || type->type != SYM_FN) return 0; type = get_base_type(type); - if (type->ctype.base_type == &int_type) { + if (type && type->ctype.base_type == &int_type) { return 1; } return 0; diff --git a/usr/src/tools/smatch/src/compat.h b/usr/src/tools/smatch/src/compat.h index 9814ae3e89..4bba47ad2a 100644 --- a/usr/src/tools/smatch/src/compat.h +++ b/usr/src/tools/smatch/src/compat.h @@ -10,8 +10,6 @@ * - "string to long double" (C99 strtold()) * Missing in Solaris and MinGW */ -struct stream; -struct stat; /* * Our "blob" allocator works on chunks that are multiples diff --git a/usr/src/tools/smatch/src/compile-i386.c b/usr/src/tools/smatch/src/compile-i386.c index 1242d38419..2ee7b35ecf 100644 --- a/usr/src/tools/smatch/src/compile-i386.c +++ b/usr/src/tools/smatch/src/compile-i386.c @@ -193,7 +193,6 @@ static const char *current_section; static void emit_comment(const char * fmt, ...) FORMAT_ATTR(1); static void emit_move(struct storage *src, struct storage *dest, struct symbol *ctype, const char *comment); -static int type_is_signed(struct symbol *sym); static struct storage *x86_address_gen(struct expression *expr); static struct storage *x86_symbol_expr(struct symbol *sym); static void x86_symbol(struct symbol *sym); @@ -452,7 +451,7 @@ static const char *stor_op_name(struct storage *s) strcpy(name, s->reg->name); break; case STOR_VALUE: - sprintf(name, "$%Ld", s->value); + sprintf(name, "$%lld", s->value); break; case STOR_LABEL: sprintf(name, "%s.L%d", s->flags & STOR_LABEL_VAL ? "$" : "", @@ -937,7 +936,7 @@ static void emit_scalar(struct expression *expr, unsigned int bit_size) assert(type != NULL); - printf("\t.%s\t%Ld\n", type, ll); + printf("\t.%s\t%lld\n", type, ll); } static void emit_global_noinit(const char *name, unsigned long modifiers, @@ -1163,7 +1162,7 @@ static void emit_move(struct storage *src, struct storage *dest, if (ctype) { bits = ctype->bit_size; - is_signed = type_is_signed(ctype); + is_signed = is_signed_type(ctype); } else { bits = 32; is_signed = 0; @@ -1355,7 +1354,7 @@ static struct storage *emit_binop(struct expression *expr) if ((expr->op == '/') || (expr->op == '%')) return emit_divide(expr, left, right); - is_signed = type_is_signed(expr->ctype); + is_signed = is_signed_type(expr->ctype); switch (expr->op) { case '+': @@ -1555,7 +1554,7 @@ static struct storage *emit_return_stmt(struct statement *stmt) static struct storage *emit_conditional_expr(struct expression *expr) { - struct storage *cond, *true = NULL, *false = NULL; + struct storage *cond, *stot = NULL, *stof = NULL; struct storage *new = stack_alloc(expr->ctype->bit_size / 8); int target_false, cond_end; @@ -1564,16 +1563,16 @@ static struct storage *emit_conditional_expr(struct expression *expr) target_false = emit_conditional_test(cond); /* handle if-true part of the expression */ - true = x86_expression(expr->cond_true); + stot = x86_expression(expr->cond_true); - emit_copy(new, true, expr->ctype); + emit_copy(new, stot, expr->ctype); cond_end = emit_conditional_end(target_false); /* handle if-false part of the expression */ - false = x86_expression(expr->cond_false); + stof = x86_expression(expr->cond_false); - emit_copy(new, false, expr->ctype); + emit_copy(new, stof, expr->ctype); /* end of conditional; jump target for if-true branch */ emit_label(cond_end, "end conditional"); @@ -1584,15 +1583,15 @@ static struct storage *emit_conditional_expr(struct expression *expr) static struct storage *emit_select_expr(struct expression *expr) { struct storage *cond = x86_expression(expr->conditional); - struct storage *true = x86_expression(expr->cond_true); - struct storage *false = x86_expression(expr->cond_false); + struct storage *stot = x86_expression(expr->cond_true); + struct storage *stof = x86_expression(expr->cond_false); struct storage *reg_cond, *reg_true, *reg_false; struct storage *new = stack_alloc(4); emit_comment("begin SELECT"); reg_cond = get_reg_value(cond, get_regclass(expr->conditional)); - reg_true = get_reg_value(true, get_regclass(expr)); - reg_false = get_reg_value(false, get_regclass(expr)); + reg_true = get_reg_value(stot, get_regclass(expr)); + reg_false = get_reg_value(stof, get_regclass(expr)); /* * Do the actual select: check the conditional for zero, @@ -2236,7 +2235,7 @@ static struct storage *x86_symbol_expr(struct symbol *sym) return new; } if (sym->ctype.modifiers & MOD_ADDRESSABLE) { - printf("\taddi.%d\t\tv%d,vFP,$%lld\n", bits_in_pointer, new->pseudo, sym->value); + printf("\taddi.%d\t\tv%d,vFP,$%lld\n", bits_in_pointer, new->pseudo, 0LL); return new; } printf("\taddi.%d\t\tv%d,vFP,$offsetof(%s:%p)\n", bits_in_pointer, new->pseudo, show_ident(sym->ident), sym); @@ -2264,15 +2263,6 @@ static void x86_symbol_init(struct symbol *sym) priv->addr = new; } -static int type_is_signed(struct symbol *sym) -{ - if (sym->type == SYM_NODE) - sym = sym->ctype.base_type; - if (sym->type == SYM_PTR) - return 0; - return !(sym->ctype.modifiers & MOD_UNSIGNED); -} - static struct storage *x86_label_expr(struct expression *expr) { struct storage *new = stack_alloc(4); diff --git a/usr/src/tools/smatch/src/compile.c b/usr/src/tools/smatch/src/compile.c index eeb996abdf..7c16d5b556 100644 --- a/usr/src/tools/smatch/src/compile.c +++ b/usr/src/tools/smatch/src/compile.c @@ -59,7 +59,7 @@ int main(int argc, char **argv) bits_in_bool = 8; clean_up_symbols(sparse_initialize(argc, argv, &filelist)); - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { struct symbol_list *list; const char *basename = strrchr(file, '/'); basename = basename ? basename+1 : file; @@ -70,7 +70,7 @@ int main(int argc, char **argv) emit_unit_begin(basename); clean_up_symbols(list); emit_unit_end(); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); #if 0 // And show the allocation statistics diff --git a/usr/src/tools/smatch/src/cse.c b/usr/src/tools/smatch/src/cse.c index 17b3da01a2..22dfd4ba5d 100644 --- a/usr/src/tools/smatch/src/cse.c +++ b/usr/src/tools/smatch/src/cse.c @@ -14,14 +14,14 @@ #include "parse.h" #include "expression.h" +#include "flowgraph.h" #include "linearize.h" #include "flow.h" +#include "cse.h" #define INSN_HASH_SIZE 256 static struct instruction_list *insn_hash_table[INSN_HASH_SIZE]; -int repeat_phase; - static int phi_compare(pseudo_t phi1, pseudo_t phi2) { const struct instruction *def1 = phi1->def; @@ -35,16 +35,10 @@ static int phi_compare(pseudo_t phi1, pseudo_t phi2) } -static void clean_up_one_instruction(struct basic_block *bb, struct instruction *insn) +void cse_collect(struct instruction *insn) { unsigned long hash; - if (!insn->bb) - return; - assert(insn->bb == bb); - repeat_phase |= simplify_instruction(insn); - if (!insn->bb) - return; hash = (insn->opcode << 3) + (insn->size >> 3); switch (insn->opcode) { case OP_SEL: @@ -53,7 +47,7 @@ static void clean_up_one_instruction(struct basic_block *bb, struct instruction /* Binary arithmetic */ case OP_ADD: case OP_SUB: - case OP_MULU: case OP_MULS: + case OP_MUL: case OP_DIVU: case OP_DIVS: case OP_MODU: case OP_MODS: case OP_SHL: @@ -61,8 +55,7 @@ static void clean_up_one_instruction(struct basic_block *bb, struct instruction case OP_AND: case OP_OR: /* Binary logical */ - case OP_XOR: case OP_AND_BOOL: - case OP_OR_BOOL: + case OP_XOR: /* Binary comparison */ case OP_SET_EQ: case OP_SET_NE: @@ -70,11 +63,20 @@ static void clean_up_one_instruction(struct basic_block *bb, struct instruction case OP_SET_LT: case OP_SET_GT: case OP_SET_B: case OP_SET_A: case OP_SET_BE: case OP_SET_AE: + + /* floating-point arithmetic & comparison */ + case OP_FPCMP ... OP_FPCMP_END: + case OP_FADD: + case OP_FSUB: + case OP_FMUL: + case OP_FDIV: hash += hashval(insn->src2); /* Fall through */ /* Unary */ case OP_NOT: case OP_NEG: + case OP_FNEG: + case OP_SYMADDR: hash += hashval(insn->src1); break; @@ -82,21 +84,20 @@ static void clean_up_one_instruction(struct basic_block *bb, struct instruction hash += hashval(insn->val); break; - case OP_SYMADDR: - hash += hashval(insn->symbol); + case OP_SETFVAL: + hash += hashval(insn->fvalue); break; - case OP_CAST: - case OP_SCAST: + case OP_SEXT: case OP_ZEXT: + case OP_TRUNC: case OP_PTRCAST: - /* - * This is crap! Many "orig_types" are the - * same as far as casts go, we should generate - * some kind of "type hash" that is identical - * for identical casts - */ - hash += hashval(insn->orig_type); + case OP_UTPTR: case OP_PTRTU: + if (!insn->orig_type || insn->orig_type->bit_size < 0) + return; hash += hashval(insn->src); + + // Note: see corresponding line in insn_compare() + hash += hashval(insn->orig_type->bit_size); break; /* Other */ @@ -125,18 +126,6 @@ static void clean_up_one_instruction(struct basic_block *bb, struct instruction add_instruction(insn_hash_table + hash, insn); } -static void clean_up_insns(struct entrypoint *ep) -{ - struct basic_block *bb; - - FOR_EACH_PTR(ep->bbs, bb) { - struct instruction *insn; - FOR_EACH_PTR(bb->insns, insn) { - clean_up_one_instruction(bb, insn); - } END_FOR_EACH_PTR(insn); - } END_FOR_EACH_PTR(bb); -} - /* Compare two (sorted) phi-lists */ static int phi_list_compare(struct pseudo_list *l1, struct pseudo_list *l2) { @@ -171,6 +160,8 @@ static int insn_compare(const void *_i1, const void *_i2) { const struct instruction *i1 = _i1; const struct instruction *i2 = _i2; + int size1, size2; + int diff; if (i1->opcode != i2->opcode) return i1->opcode < i2->opcode ? -1 : 1; @@ -179,8 +170,7 @@ static int insn_compare(const void *_i1, const void *_i2) /* commutative binop */ case OP_ADD: - case OP_MULU: case OP_MULS: - case OP_AND_BOOL: case OP_OR_BOOL: + case OP_MUL: case OP_AND: case OP_OR: case OP_XOR: case OP_SET_EQ: case OP_SET_NE: @@ -205,6 +195,13 @@ static int insn_compare(const void *_i1, const void *_i2) case OP_SET_LT: case OP_SET_GT: case OP_SET_B: case OP_SET_A: case OP_SET_BE: case OP_SET_AE: + + /* floating-point arithmetic */ + case OP_FPCMP ... OP_FPCMP_END: + case OP_FADD: + case OP_FSUB: + case OP_FMUL: + case OP_FDIV: case_binops: if (i1->src2 != i2->src2) return i1->src2 < i2->src2 ? -1 : 1; @@ -212,34 +209,43 @@ static int insn_compare(const void *_i1, const void *_i2) /* Unary */ case OP_NOT: case OP_NEG: + case OP_FNEG: + case OP_SYMADDR: if (i1->src1 != i2->src1) return i1->src1 < i2->src1 ? -1 : 1; break; - case OP_SYMADDR: - if (i1->symbol != i2->symbol) - return i1->symbol < i2->symbol ? -1 : 1; - break; - case OP_SETVAL: if (i1->val != i2->val) return i1->val < i2->val ? -1 : 1; break; + case OP_SETFVAL: + diff = memcmp(&i1->fvalue, &i2->fvalue, sizeof(i1->fvalue)); + if (diff) + return diff; + break; + /* Other */ case OP_PHI: return phi_list_compare(i1->phi_list, i2->phi_list); - case OP_CAST: - case OP_SCAST: + case OP_SEXT: case OP_ZEXT: + case OP_TRUNC: case OP_PTRCAST: - /* - * This is crap! See the comments on hashing. - */ - if (i1->orig_type != i2->orig_type) - return i1->orig_type < i2->orig_type ? -1 : 1; + case OP_UTPTR: case OP_PTRTU: if (i1->src != i2->src) return i1->src < i2->src ? -1 : 1; + + // Note: if it can be guaranted that identical ->src + // implies identical orig_type->bit_size, then this + // test and the hashing of the original size in + // cse_collect() are not needed. + // It must be generaly true but it isn't guaranted (yet). + size1 = i1->orig_type->bit_size; + size2 = i2->orig_type->bit_size; + if (size1 != size2) + return size1 < size2 ? -1 : 1; break; default: @@ -264,28 +270,6 @@ static struct instruction * cse_one_instruction(struct instruction *insn, struct return def; } -/* - * Does "bb1" dominate "bb2"? - */ -static int bb_dominates(struct entrypoint *ep, struct basic_block *bb1, struct basic_block *bb2, unsigned long generation) -{ - struct basic_block *parent; - - /* Nothing dominates the entrypoint.. */ - if (bb2 == ep->entry->bb) - return 0; - FOR_EACH_PTR(bb2->parents, parent) { - if (parent == bb1) - continue; - if (parent->generation == generation) - continue; - parent->generation = generation; - if (!bb_dominates(ep, bb1, parent, generation)) - return 0; - } END_FOR_EACH_PTR(parent); - return 1; -} - static struct basic_block *trivial_common_parent(struct basic_block *bb1, struct basic_block *bb2) { struct basic_block *parent; @@ -339,10 +323,10 @@ static struct instruction * try_to_cse(struct entrypoint *ep, struct instruction warning(b1->pos, "Whaa? unable to find CSE instructions"); return i1; } - if (bb_dominates(ep, b1, b2, ++bb_generation)) + if (domtree_dominates(b1, b2)) return cse_one_instruction(i2, i1); - if (bb_dominates(ep, b2, b1, ++bb_generation)) + if (domtree_dominates(b2, b1)) return cse_one_instruction(i1, i2); /* No direct dominance - but we could try to find a common ancestor.. */ @@ -351,21 +335,17 @@ static struct instruction * try_to_cse(struct entrypoint *ep, struct instruction i1 = cse_one_instruction(i2, i1); remove_instruction(&b1->insns, i1, 1); add_instruction_to_end(i1, common); + } else { + i1 = i2; } return i1; } -void cleanup_and_cse(struct entrypoint *ep) +void cse_eliminate(struct entrypoint *ep) { int i; - simplify_memops(ep); -repeat: - repeat_phase = 0; - clean_up_insns(ep); - if (repeat_phase & REPEAT_CFG_CLEANUP) - kill_unreachable_bbs(ep); for (i = 0; i < INSN_HASH_SIZE; i++) { struct instruction_list **list = insn_hash_table + i; if (*list) { @@ -385,13 +365,7 @@ repeat: last = insn; } END_FOR_EACH_PTR(insn); } - free_ptr_list((struct ptr_list **)list); + free_ptr_list(list); } } - - if (repeat_phase & REPEAT_SYMBOL_CLEANUP) - simplify_memops(ep); - - if (repeat_phase & REPEAT_CSE) - goto repeat; } diff --git a/usr/src/tools/smatch/src/cse.h b/usr/src/tools/smatch/src/cse.h new file mode 100644 index 0000000000..29c97ea9d2 --- /dev/null +++ b/usr/src/tools/smatch/src/cse.h @@ -0,0 +1,11 @@ +#ifndef CSE_H +#define CSE_H + +struct instruction; +struct entrypoint; + +/* cse.c */ +void cse_collect(struct instruction *insn); +void cse_eliminate(struct entrypoint *ep); + +#endif diff --git a/usr/src/tools/smatch/src/ctags.c b/usr/src/tools/smatch/src/ctags.c index 9ec6b3c37e..aa5f9718d8 100644 --- a/usr/src/tools/smatch/src/ctags.c +++ b/usr/src/tools/smatch/src/ctags.c @@ -135,7 +135,7 @@ static void examine_symbol(struct symbol *sym) switch (sym->type) { case SYM_NODE: - if (base->type == SYM_FN) + if (base && base->type == SYM_FN) sym->kind = 'f'; examine_symbol(base); break; @@ -216,10 +216,10 @@ int main(int argc, char **argv) char *file; examine_symbol_list(sparse_initialize(argc, argv, &filelist)); - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { sparse(file); examine_symbol_list(file_scope->symbols); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); examine_symbol_list(global_scope->symbols); sort_list((struct ptr_list **)&taglist, cmp_sym); show_tags(taglist); diff --git a/usr/src/tools/smatch/src/dominate.c b/usr/src/tools/smatch/src/dominate.c new file mode 100644 index 0000000000..bf2ae63a9e --- /dev/null +++ b/usr/src/tools/smatch/src/dominate.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: MIT +// +// dominate.c - compute the (iterated) dominance frontier of (a set of) nodes. +// +// Copyright (C) 2017 - Luc Van Oostenryck +// +// The algorithm used is the one described in: +// "A Linear Time Algorithm for Placing phi-nodes" +// by Vugranam C. Sreedhar and Guang R. Gao +// + +#include "dominate.h" +#include "flowgraph.h" +#include "linearize.h" +#include "flow.h" +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + + +struct piggy { + unsigned int max; + struct basic_block_list *lists[0]; +}; + +static struct piggy *bank_init(unsigned levels) +{ + struct piggy *bank; + bank = calloc(1, sizeof(*bank) + levels * sizeof(bank->lists[0])); + bank->max = levels - 1; + return bank; +} + +static void bank_free(struct piggy *bank, unsigned int levels) +{ + for (; levels-- ;) + free_ptr_list(&bank->lists[levels]); + free(bank); +} + +static void bank_put(struct piggy *bank, struct basic_block *bb) +{ + unsigned int level = bb->dom_level; + assert(level <= bank->max); + add_bb(&bank->lists[level], bb); +} + +static inline struct basic_block *pop_bb(struct basic_block_list **list) +{ + return delete_ptr_list_last((struct ptr_list **)list); +} + +static struct basic_block *bank_get(struct piggy *bank) +{ + int level = bank->max; + do { + struct basic_block *bb = pop_bb(&bank->lists[level]); + if (bb) + return bb; + if (!level) + return NULL; + bank->max = --level; + } while (1); +} + + +#define VISITED 0x1 +#define INPHI 0x2 +#define ALPHA 0x4 +#define FLAGS 0x7 + +static void visit(struct piggy *bank, struct basic_block_list **idf, struct basic_block *x, int curr_level) +{ + struct basic_block *y; + + x->generation |= 1; + FOR_EACH_PTR(x->children, y) { + unsigned flags = y->generation & FLAGS; + if (y->idom == x) // J-edges will be processed later + continue; + if (y->dom_level > curr_level) + continue; + if (flags & INPHI) + continue; + y->generation |= INPHI; + add_bb(idf, y); + if (flags & ALPHA) + continue; + bank_put(bank, y); + } END_FOR_EACH_PTR(y); + + FOR_EACH_PTR(x->doms, y) { + if (y->generation & VISITED) + continue; + visit(bank, idf, y, curr_level); + } END_FOR_EACH_PTR(y); +} + +void idf_compute(struct entrypoint *ep, struct basic_block_list **idf, struct basic_block_list *alpha) +{ + int levels = ep->dom_levels; + struct piggy *bank = bank_init(levels); + struct basic_block *bb; + unsigned long generation = bb_generation; + + generation = bb_generation; + generation += -generation & FLAGS; + bb_generation = generation + (FLAGS + 1); + + // init all the nodes + FOR_EACH_PTR(ep->bbs, bb) { + // FIXME: this should be removed and the tests for + // visited/in_phi/alpha should use a sparse set + bb->generation = generation; + } END_FOR_EACH_PTR(bb); + + FOR_EACH_PTR(alpha, bb) { + bb->generation = generation | ALPHA; + bank_put(bank, bb); + } END_FOR_EACH_PTR(bb); + + while ((bb = bank_get(bank))) { + visit(bank, idf, bb, bb->dom_level); + } + + bank_free(bank, levels); +} + +void idf_dump(struct entrypoint *ep) +{ + struct basic_block *bb; + + domtree_build(ep); + + printf("%s's IDF:\n", show_ident(ep->name->ident)); + FOR_EACH_PTR(ep->bbs, bb) { + struct basic_block_list *alpha = NULL; + struct basic_block_list *idf = NULL; + struct basic_block *df; + + add_bb(&alpha, bb); + idf_compute(ep, &idf, alpha); + + printf("\t%s\t<-", show_label(bb)); + FOR_EACH_PTR(idf, df) { + printf(" %s", show_label(df)); + } END_FOR_EACH_PTR(df); + printf("\n"); + + free_ptr_list(&idf); + free_ptr_list(&alpha); + } END_FOR_EACH_PTR(bb); +} diff --git a/usr/src/tools/smatch/src/dominate.h b/usr/src/tools/smatch/src/dominate.h new file mode 100644 index 0000000000..a06216caf5 --- /dev/null +++ b/usr/src/tools/smatch/src/dominate.h @@ -0,0 +1,13 @@ +#ifndef DOMINATE_H +#define DOMINATE_H + +struct entrypoint; +struct basic_block_list; + +void idf_compute(struct entrypoint *ep, struct basic_block_list **idf, struct basic_block_list *alpha); + + +// For debugging only +void idf_dump(struct entrypoint *ep); + +#endif diff --git a/usr/src/tools/smatch/src/evaluate.c b/usr/src/tools/smatch/src/evaluate.c index 14abc8fa5c..acd4556fa0 100644 --- a/usr/src/tools/smatch/src/evaluate.c +++ b/usr/src/tools/smatch/src/evaluate.c @@ -34,6 +34,7 @@ #include <fcntl.h> #include <limits.h> +#include "evaluate.h" #include "lib.h" #include "allocate.h" #include "parse.h" @@ -44,9 +45,22 @@ struct symbol *current_fn; +struct ident bad_address_space = { .len = 6, .name = "bad AS", }; + static struct symbol *degenerate(struct expression *expr); static struct symbol *evaluate_symbol(struct symbol *sym); +static inline int valid_expr_type(struct expression *expr) +{ + return expr && valid_type(expr->ctype); +} + +static inline int valid_subexpr_type(struct expression *expr) +{ + return valid_expr_type(expr->left) + && valid_expr_type(expr->right); +} + static struct symbol *evaluate_symbol_expression(struct expression *expr) { struct expression *addr; @@ -196,14 +210,14 @@ static int same_cast_type(struct symbol *orig, struct symbol *new) orig->bit_offset == new->bit_offset; } -static struct symbol *base_type(struct symbol *node, unsigned long *modp, unsigned long *asp) +static struct symbol *base_type(struct symbol *node, unsigned long *modp, struct ident **asp) { - unsigned long mod, as; + unsigned long mod = 0; + struct ident *as = NULL; - mod = 0; as = 0; while (node) { mod |= node->ctype.modifiers; - as |= node->ctype.as; + combine_address_space(node->pos, &as, node->ctype.as); if (node->type == SYM_NODE) { node = node->ctype.base_type; continue; @@ -218,7 +232,8 @@ static struct symbol *base_type(struct symbol *node, unsigned long *modp, unsign static int is_same_type(struct expression *expr, struct symbol *new) { struct symbol *old = expr->ctype; - unsigned long oldmod, newmod, oldas, newas; + unsigned long oldmod, newmod; + struct ident *oldas, *newas; old = base_type(old, &oldmod, &oldas); new = base_type(new, &newmod, &newas); @@ -393,15 +408,20 @@ static inline int is_string_type(struct symbol *type) static struct symbol *bad_expr_type(struct expression *expr) { - sparse_error(expr->pos, "incompatible types for operation (%s)", show_special(expr->op)); switch (expr->type) { case EXPR_BINOP: case EXPR_COMPARE: + if (!valid_subexpr_type(expr)) + break; + sparse_error(expr->pos, "incompatible types for operation (%s)", show_special(expr->op)); info(expr->pos, " left side has type %s", show_typename(expr->left->ctype)); info(expr->pos, " right side has type %s", show_typename(expr->right->ctype)); break; case EXPR_PREOP: case EXPR_POSTOP: + if (!valid_expr_type(expr->unop)) + break; + sparse_error(expr->pos, "incompatible types for operation (%s)", show_special(expr->op)); info(expr->pos, " argument has type %s", show_typename(expr->unop->ctype)); break; default: @@ -638,12 +658,12 @@ static struct symbol *evaluate_ptr_add(struct expression *expr, struct symbol *i static void examine_fn_arguments(struct symbol *fn); -#define MOD_IGN (MOD_VOLATILE | MOD_CONST | MOD_PURE) +#define MOD_IGN (MOD_QUALIFIER | MOD_PURE) const char *type_difference(struct ctype *c1, struct ctype *c2, unsigned long mod1, unsigned long mod2) { - unsigned long as1 = c1->as, as2 = c2->as; + struct ident *as1 = c1->as, *as2 = c2->as; struct symbol *t1 = c1->base_type; struct symbol *t2 = c2->base_type; int move1 = 1, move2 = 1; @@ -661,7 +681,7 @@ const char *type_difference(struct ctype *c1, struct ctype *c2, if (move1) { if (t1 && t1->type != SYM_PTR) { mod1 |= t1->ctype.modifiers; - as1 |= t1->ctype.as; + combine_address_space(t1->pos, &as1, t1->ctype.as); } move1 = 0; } @@ -669,7 +689,7 @@ const char *type_difference(struct ctype *c1, struct ctype *c2, if (move2) { if (t2 && t2->type != SYM_PTR) { mod2 |= t2->ctype.modifiers; - as2 |= t2->ctype.as; + combine_address_space(t2->pos, &as2, t2->ctype.as); } move2 = 0; } @@ -847,8 +867,10 @@ static struct symbol *evaluate_ptr_sub(struct expression *expr) val->value = value; if (value & (value-1)) { - if (Wptr_subtraction_blows) + if (Wptr_subtraction_blows) { warning(expr->pos, "potentially expensive pointer subtraction"); + info(expr->pos, " '%s' has a non-power-of-2 size: %lu", show_typename(lbase), value); + } } sub->op = '-'; @@ -877,23 +899,23 @@ static struct symbol *evaluate_conditional(struct expression *expr, int iterator warning(expr->pos, "assignment expression in conditional"); ctype = evaluate_expression(expr); - if (ctype) { - if (is_safe_type(ctype)) - warning(expr->pos, "testing a 'safe expression'"); - if (is_func_type(ctype)) { - if (Waddress) - warning(expr->pos, "the address of %s will always evaluate as true", "a function"); - } else if (is_array_type(ctype)) { - if (Waddress) - warning(expr->pos, "the address of %s will always evaluate as true", "an array"); - } else if (!is_scalar_type(ctype)) { - sparse_error(expr->pos, "incorrect type in conditional"); - info(expr->pos, " got %s", show_typename(ctype)); - ctype = NULL; - } + if (!valid_type(ctype)) + return NULL; + if (is_safe_type(ctype)) + warning(expr->pos, "testing a 'safe expression'"); + if (is_func_type(ctype)) { + if (Waddress) + warning(expr->pos, "the address of %s will always evaluate as true", "a function"); + } else if (is_array_type(ctype)) { + if (Waddress) + warning(expr->pos, "the address of %s will always evaluate as true", "an array"); + } else if (!is_scalar_type(ctype)) { + sparse_error(expr->pos, "incorrect type in conditional (non-scalar type)"); + info(expr->pos, " got %s", show_typename(ctype)); + return NULL; } - ctype = degenerate(expr); + ctype = degenerate(expr); return ctype; } @@ -1005,13 +1027,19 @@ static int modify_for_unsigned(int op) return op; } +enum null_constant_type { + NON_NULL, + NULL_PTR, + NULL_ZERO, +}; + static inline int is_null_pointer_constant(struct expression *e) { if (e->ctype == &null_ctype) - return 1; + return NULL_PTR; if (!(e->flags & CEF_ICE)) - return 0; - return is_zero_constant(e) ? 2 : 0; + return NON_NULL; + return is_zero_constant(e) ? NULL_ZERO : NON_NULL; } static struct symbol *evaluate_compare(struct expression *expr) @@ -1057,9 +1085,9 @@ static struct symbol *evaluate_compare(struct expression *expr) if (expr->op == SPECIAL_EQUAL || expr->op == SPECIAL_NOTEQUAL) { int is_null1 = is_null_pointer_constant(left); int is_null2 = is_null_pointer_constant(right); - if (is_null1 == 2) + if (is_null1 == NULL_ZERO) bad_null(left); - if (is_null2 == 2) + if (is_null2 == NULL_ZERO) bad_null(right); if (is_null1 && is_null2) { int positive = expr->op == SPECIAL_EQUAL; @@ -1104,7 +1132,9 @@ static struct symbol *evaluate_compare(struct expression *expr) if (!typediff) goto OK; - expression_error(expr, "incompatible types in comparison expression (%s)", typediff); + expression_error(expr, "incompatible types in comparison expression (%s):", typediff); + info(expr->pos, " %s", show_typename(ltype)); + info(expr->pos, " %s", show_typename(rtype)); return NULL; OK: @@ -1122,7 +1152,7 @@ OK: */ static struct symbol *evaluate_conditional_expression(struct expression *expr) { - struct expression **true; + struct expression **cond; struct symbol *ctype, *ltype, *rtype, *lbase, *rbase; int lclass, rclass; const char * typediff; @@ -1136,16 +1166,16 @@ static struct symbol *evaluate_conditional_expression(struct expression *expr) ctype = degenerate(expr->conditional); rtype = degenerate(expr->cond_false); - true = &expr->conditional; + cond = &expr->conditional; ltype = ctype; if (expr->cond_true) { if (!evaluate_expression(expr->cond_true)) return NULL; ltype = degenerate(expr->cond_true); - true = &expr->cond_true; + cond = &expr->cond_true; } - expr->flags = (expr->conditional->flags & (*true)->flags & + expr->flags = (expr->conditional->flags & (*cond)->flags & expr->cond_false->flags & ~CEF_CONST_MASK); /* * A conditional operator yields a particular constant @@ -1161,37 +1191,37 @@ static struct symbol *evaluate_conditional_expression(struct expression *expr) * address constants, mark the result as an address constant. */ if (expr->conditional->flags & (CEF_ACE | CEF_ADDR)) - expr->flags = (*true)->flags & expr->cond_false->flags & ~CEF_CONST_MASK; + expr->flags = (*cond)->flags & expr->cond_false->flags & ~CEF_CONST_MASK; lclass = classify_type(ltype, <ype); rclass = classify_type(rtype, &rtype); if (lclass & rclass & TYPE_NUM) { - ctype = usual_conversions('?', *true, expr->cond_false, + ctype = usual_conversions('?', *cond, expr->cond_false, lclass, rclass, ltype, rtype); - *true = cast_to(*true, ctype); + *cond = cast_to(*cond, ctype); expr->cond_false = cast_to(expr->cond_false, ctype); goto out; } if ((lclass | rclass) & TYPE_PTR) { - int is_null1 = is_null_pointer_constant(*true); + int is_null1 = is_null_pointer_constant(*cond); int is_null2 = is_null_pointer_constant(expr->cond_false); if (is_null1 && is_null2) { - *true = cast_to(*true, &ptr_ctype); + *cond = cast_to(*cond, &ptr_ctype); expr->cond_false = cast_to(expr->cond_false, &ptr_ctype); ctype = &ptr_ctype; goto out; } if (is_null1 && (rclass & TYPE_PTR)) { - if (is_null1 == 2) - bad_null(*true); - *true = cast_to(*true, rtype); + if (is_null1 == NULL_ZERO) + bad_null(*cond); + *cond = cast_to(*cond, rtype); ctype = rtype; goto out; } if (is_null2 && (lclass & TYPE_PTR)) { - if (is_null2 == 2) + if (is_null2 == NULL_ZERO) bad_null(expr->cond_false); expr->cond_false = cast_to(expr->cond_false, ltype); ctype = ltype; @@ -1240,7 +1270,9 @@ static struct symbol *evaluate_conditional_expression(struct expression *expr) typediff = "different base types"; Err: - expression_error(expr, "incompatible types in conditional expression (%s)", typediff); + expression_error(expr, "incompatible types in conditional expression (%s):", typediff); + info(expr->pos, " %s", show_typename(ltype)); + info(expr->pos, " %s", show_typename(rtype)); /* * if the condition is constant, the type is in fact known * so use it, as gcc & clang do. @@ -1266,7 +1298,7 @@ Qual: sym->ctype.modifiers |= qual; ctype = sym; } - *true = cast_to(*true, ctype); + *cond = cast_to(*cond, ctype); expr->cond_false = cast_to(expr->cond_false, ctype); goto out; } @@ -1306,6 +1338,11 @@ static int evaluate_assign_op(struct expression *expr) goto Cast; if (!restricted_value(expr->right, t)) return 1; + } else if (op == SPECIAL_SHR_ASSIGN || op == SPECIAL_SHL_ASSIGN) { + // shifts do integer promotions, but that's it. + unrestrict(expr->right, sclass, &s); + target = integer_promotion(s); + goto Cast; } else if (!(sclass & TYPE_RESTRICT)) goto usual; /* source and target would better be identical restricted */ @@ -1394,7 +1431,7 @@ static int check_assignment_types(struct symbol *target, struct expression **rp, // NULL pointer is always OK int is_null = is_null_pointer_constant(*rp); if (is_null) { - if (is_null == 2) + if (is_null == NULL_ZERO) bad_null(*rp); goto Cast; } @@ -1544,7 +1581,6 @@ static void evaluate_assign_to(struct expression *left, struct symbol *type) static struct symbol *evaluate_assignment(struct expression *expr) { struct expression *left = expr->left; - struct expression *where = expr; struct symbol *ltype; if (!lvalue_expression(left)) { @@ -1558,7 +1594,7 @@ static struct symbol *evaluate_assignment(struct expression *expr) if (!evaluate_assign_op(expr)) return NULL; } else { - if (!compatible_assignment_types(where, ltype, &expr->right, "assignment")) + if (!compatible_assignment_types(expr, ltype, &expr->right, "assignment")) return NULL; } @@ -1585,11 +1621,11 @@ static void examine_fn_arguments(struct symbol *fn) ptr->ctype = arg->ctype; else ptr->ctype.base_type = arg; - ptr->ctype.as |= s->ctype.as; + combine_address_space(s->pos, &ptr->ctype.as, s->ctype.as); ptr->ctype.modifiers |= s->ctype.modifiers & MOD_PTRINHERIT; s->ctype.base_type = ptr; - s->ctype.as = 0; + s->ctype.as = NULL; s->ctype.modifiers &= ~MOD_PTRINHERIT; s->bit_size = 0; s->examined = 0; @@ -1603,7 +1639,7 @@ static void examine_fn_arguments(struct symbol *fn) } END_FOR_EACH_PTR(s); } -static struct symbol *convert_to_as_mod(struct symbol *sym, int as, int mod) +static struct symbol *convert_to_as_mod(struct symbol *sym, struct ident *as, int mod) { /* Take the modifiers of the pointer, and apply them to the member */ mod |= sym->ctype.modifiers; @@ -1635,12 +1671,12 @@ static struct symbol *create_pointer(struct expression *expr, struct symbol *sym sym->ctype.modifiers &= ~MOD_REGISTER; } if (sym->type == SYM_NODE) { - ptr->ctype.as |= sym->ctype.as; + combine_address_space(sym->pos, &ptr->ctype.as, sym->ctype.as); ptr->ctype.modifiers |= sym->ctype.modifiers & MOD_PTRINHERIT; sym = sym->ctype.base_type; } if (degenerate && sym->type == SYM_ARRAY) { - ptr->ctype.as |= sym->ctype.as; + combine_address_space(sym->pos, &ptr->ctype.as, sym->ctype.as); ptr->ctype.modifiers |= sym->ctype.modifiers & MOD_PTRINHERIT; sym = sym->ctype.base_type; } @@ -1776,14 +1812,18 @@ static struct symbol *evaluate_dereference(struct expression *expr) if (ctype->type == SYM_NODE) ctype = ctype->ctype.base_type; - node = alloc_symbol(expr->pos, SYM_NODE); target = ctype->ctype.base_type; + examine_symbol_type(target); switch (ctype->type) { default: expression_error(expr, "cannot dereference this type"); return NULL; + case SYM_FN: + *expr = *op; + return expr->ctype; case SYM_PTR: + node = alloc_symbol(expr->pos, SYM_NODE); node->ctype.modifiers = target->ctype.modifiers & MOD_SPECIFIER; merge_type(node, ctype); break; @@ -1801,6 +1841,7 @@ static struct symbol *evaluate_dereference(struct expression *expr) * When an array is dereferenced, we need to pick * up the attributes of the original node too.. */ + node = alloc_symbol(expr->pos, SYM_NODE); merge_type(node, op->ctype); merge_type(node, ctype); break; @@ -1914,6 +1955,7 @@ static struct symbol *evaluate_preop(struct expression *expr) return evaluate_postop(expr); case '!': + ctype = degenerate(expr->unop); expr->flags = expr->unop->flags & ~CEF_CONST_MASK; /* * A logical negation never yields an address constant @@ -2021,8 +2063,8 @@ static struct symbol *evaluate_member_dereference(struct expression *expr) struct symbol *ctype, *member; struct expression *deref = expr->deref, *add; struct ident *ident = expr->member; + struct ident *address_space; unsigned int mod; - int address_space; if (!evaluate_expression(deref)) return NULL; @@ -2037,7 +2079,7 @@ static struct symbol *evaluate_member_dereference(struct expression *expr) mod = ctype->ctype.modifiers; if (ctype->type == SYM_NODE) { ctype = ctype->ctype.base_type; - address_space |= ctype->ctype.as; + combine_address_space(deref->pos, &address_space, ctype->ctype.as); mod |= ctype->ctype.modifiers; } if (!ctype || (ctype->type != SYM_STRUCT && ctype->type != SYM_UNION)) { @@ -2174,10 +2216,10 @@ static struct symbol *evaluate_sizeof(struct expression *expr) size = bits_in_char; } - if (size == 1 && is_bool_type(type)) { + if (is_bool_type(type)) { if (Wsizeof_bool) - warning(expr->pos, "expression using sizeof bool"); - size = bits_in_char; + warning(expr->pos, "expression using sizeof _Bool"); + size = bits_to_bytes(bits_in_bool) * bits_in_char; } if (is_function(type->ctype.base_type)) { @@ -2186,6 +2228,38 @@ static struct symbol *evaluate_sizeof(struct expression *expr) size = bits_in_char; } + if (is_array_type(type) && size < 0) { // VLA, 1-dimension only + struct expression *base, *size; + struct symbol *base_type; + + if (type->type == SYM_NODE) + type = type->ctype.base_type; // strip the SYM_NODE + base_type = get_base_type(type); + if (!base_type) + goto error; + if (base_type->bit_size <= 0) { + base = alloc_expression(expr->pos, EXPR_SIZEOF); + base->cast_type = base_type; + if (!evaluate_sizeof(base)) + goto error; + } else { + base = alloc_expression(expr->pos, EXPR_VALUE); + base->value = bits_to_bytes(base_type->bit_size); + base->ctype = size_t_ctype; + } + size = alloc_expression(expr->pos, EXPR_CAST); + size->cast_type = size_t_ctype; + size->cast_expression = type->array_size; + if (!evaluate_expression(size)) + goto error; + expr->left = size; + expr->right = base; + expr->type = EXPR_BINOP; + expr->op = '*'; + return expr->ctype = size_t_ctype; + } + +error: if ((size < 0) || (size & (bits_in_char - 1))) expression_error(expr, "cannot size expression"); @@ -2847,13 +2921,13 @@ static int cast_flags(struct expression *expr, struct expression *old) static struct symbol *evaluate_cast(struct expression *expr) { - struct expression *target = expr->cast_expression; + struct expression *source = expr->cast_expression; struct symbol *ctype; - struct symbol *t1, *t2; - int class1, class2; - int as1 = 0, as2 = 0; + struct symbol *ttype, *stype; + int tclass, sclass; + struct ident *tas = NULL, *sas = NULL; - if (!target) + if (!source) return NULL; /* @@ -2866,11 +2940,11 @@ static struct symbol *evaluate_cast(struct expression *expr) * dereferenced as part of a post-fix expression. * We need to produce an expression that can be dereferenced. */ - if (target->type == EXPR_INITIALIZER) { + if (source->type == EXPR_INITIALIZER) { struct symbol *sym = expr->cast_type; struct expression *addr = alloc_expression(expr->pos, EXPR_SYMBOL); - sym->initializer = target; + sym->initializer = source; evaluate_symbol(sym); addr->ctype = &lazy_ptr_ctype; /* Lazy eval */ @@ -2890,84 +2964,84 @@ static struct symbol *evaluate_cast(struct expression *expr) expr->ctype = ctype; expr->cast_type = ctype; - evaluate_expression(target); - degenerate(target); + evaluate_expression(source); + degenerate(source); - class1 = classify_type(ctype, &t1); + tclass = classify_type(ctype, &ttype); - expr->flags = cast_flags(expr, target); + expr->flags = cast_flags(expr, source); /* * You can always throw a value away by casting to * "void" - that's an implicit "force". Note that * the same is _not_ true of "void *". */ - if (t1 == &void_ctype) + if (ttype == &void_ctype) goto out; - if (class1 & (TYPE_COMPOUND | TYPE_FN)) - warning(expr->pos, "cast to non-scalar"); - - t2 = target->ctype; - if (!t2) { + stype = source->ctype; + if (!stype) { expression_error(expr, "cast from unknown type"); goto out; } - class2 = classify_type(t2, &t2); - - if (class2 & TYPE_COMPOUND) - warning(expr->pos, "cast from non-scalar"); + sclass = classify_type(stype, &stype); if (expr->type == EXPR_FORCE_CAST) goto out; + if (tclass & (TYPE_COMPOUND | TYPE_FN)) + warning(expr->pos, "cast to non-scalar"); + + if (sclass & TYPE_COMPOUND) + warning(expr->pos, "cast from non-scalar"); + /* allowed cast unfouls */ - if (class2 & TYPE_FOULED) - t2 = unfoul(t2); + if (sclass & TYPE_FOULED) + stype = unfoul(stype); - if (t1 != t2) { - if ((class1 & TYPE_RESTRICT) && restricted_value(target, t1)) + if (ttype != stype) { + if ((tclass & TYPE_RESTRICT) && restricted_value(source, ttype)) warning(expr->pos, "cast to %s", - show_typename(t1)); - if (class2 & TYPE_RESTRICT) { - if (t1 == &bool_ctype) { - if (class2 & TYPE_FOULED) + show_typename(ttype)); + if (sclass & TYPE_RESTRICT) { + if (ttype == &bool_ctype) { + if (sclass & TYPE_FOULED) warning(expr->pos, "%s degrades to integer", - show_typename(t2)); + show_typename(stype)); } else { warning(expr->pos, "cast from %s", - show_typename(t2)); + show_typename(stype)); } } } - if (t1 == &ulong_ctype) - as1 = -1; - else if (class1 == TYPE_PTR) { - examine_pointer_target(t1); - as1 = t1->ctype.as; + if ((ttype == &ulong_ctype || ttype == uintptr_ctype) && !Wcast_from_as) + tas = &bad_address_space; + else if (tclass == TYPE_PTR) { + examine_pointer_target(ttype); + tas = ttype->ctype.as; } - if (t2 == &ulong_ctype) - as2 = -1; - else if (class2 == TYPE_PTR) { - examine_pointer_target(t2); - as2 = t2->ctype.as; + if ((stype == &ulong_ctype || stype == uintptr_ctype)) + sas = &bad_address_space; + else if (sclass == TYPE_PTR) { + examine_pointer_target(stype); + sas = stype->ctype.as; } - if (!as1 && as2 > 0) - warning(expr->pos, "cast removes address space of expression"); - if (as1 > 0 && as2 > 0 && as1 != as2) - warning(expr->pos, "cast between address spaces (<asn:%d>-><asn:%d>)", as2, as1); - if (as1 > 0 && !as2 && - !is_null_pointer_constant(target) && Wcast_to_as) + if (!tas && valid_as(sas)) + warning(expr->pos, "cast removes address space '%s' of expression", show_as(sas)); + if (valid_as(tas) && valid_as(sas) && tas != sas) + warning(expr->pos, "cast between address spaces (%s -> %s)", show_as(sas), show_as(tas)); + if (valid_as(tas) && !sas && + !is_null_pointer_constant(source) && Wcast_to_as) warning(expr->pos, - "cast adds address space to expression (<asn:%d>)", as1); + "cast adds address space '%s' to expression", show_as(tas)); - if (!(t1->ctype.modifiers & MOD_PTRINHERIT) && class1 == TYPE_PTR && - !as1 && (target->flags & CEF_ICE)) { - if (t1->ctype.base_type == &void_ctype) { - if (is_zero_constant(target)) { + if (!(ttype->ctype.modifiers & MOD_PTRINHERIT) && tclass == TYPE_PTR && + !tas && (source->flags & CEF_ICE)) { + if (ttype->ctype.base_type == &void_ctype) { + if (is_zero_constant(source)) { /* NULL */ expr->type = EXPR_VALUE; expr->ctype = &null_ctype; @@ -2977,9 +3051,28 @@ static struct symbol *evaluate_cast(struct expression *expr) } } - if (t1 == &bool_ctype) + if (ttype == &bool_ctype) cast_to_bool(expr); + // checks pointers to restricted + while (Wbitwise_pointer && tclass == TYPE_PTR && sclass == TYPE_PTR) { + tclass = classify_type(ttype->ctype.base_type, &ttype); + sclass = classify_type(stype->ctype.base_type, &stype); + if (ttype == stype) + break; + if (!ttype || !stype) + break; + if (ttype == &void_ctype || stype == &void_ctype) + break; + if (tclass & TYPE_RESTRICT) { + warning(expr->pos, "cast to %s", show_typename(ctype)); + break; + } + if (sclass & TYPE_RESTRICT) { + warning(expr->pos, "cast from %s", show_typename(source->ctype)); + break; + } + } out: return ctype; } @@ -3183,9 +3276,9 @@ struct symbol *evaluate_expression(struct expression *expr) case EXPR_SYMBOL: return evaluate_symbol_expression(expr); case EXPR_BINOP: - if (!evaluate_expression(expr->left)) - return NULL; - if (!evaluate_expression(expr->right)) + evaluate_expression(expr->left); + evaluate_expression(expr->right); + if (!valid_subexpr_type(expr)) return NULL; return evaluate_binop(expr); case EXPR_LOGICAL: @@ -3196,15 +3289,15 @@ struct symbol *evaluate_expression(struct expression *expr) return NULL; return evaluate_comma(expr); case EXPR_COMPARE: - if (!evaluate_expression(expr->left)) - return NULL; - if (!evaluate_expression(expr->right)) + evaluate_expression(expr->left); + evaluate_expression(expr->right); + if (!valid_subexpr_type(expr)) return NULL; return evaluate_compare(expr); case EXPR_ASSIGNMENT: - if (!evaluate_expression(expr->left)) - return NULL; - if (!evaluate_expression(expr->right)) + evaluate_expression(expr->left); + evaluate_expression(expr->right); + if (!valid_subexpr_type(expr)) return NULL; return evaluate_assignment(expr); case EXPR_PREOP: @@ -3260,11 +3353,14 @@ struct symbol *evaluate_expression(struct expression *expr) case EXPR_SLICE: expression_error(expr, "internal front-end error: SLICE re-evaluated"); return NULL; + case EXPR_ASM_OPERAND: + expression_error(expr, "internal front-end error: ASM_OPERAND evaluated"); + return NULL; } return NULL; } -static void check_duplicates(struct symbol *sym) +void check_duplicates(struct symbol *sym) { int declared = 0; struct symbol *next = sym; @@ -3291,7 +3387,7 @@ static void check_duplicates(struct symbol *sym) } if (!declared) { unsigned long mod = sym->ctype.modifiers; - if (mod & (MOD_STATIC | MOD_REGISTER)) + if (mod & (MOD_STATIC | MOD_REGISTER | MOD_EXT_VISIBLE)) return; if (!(mod & MOD_TOPLEVEL)) return; @@ -3422,8 +3518,8 @@ static void verify_input_constraint(struct expression *expr, const char *constra static void evaluate_asm_statement(struct statement *stmt) { struct expression *expr; + struct expression *op; struct symbol *sym; - int state; expr = stmt->asm_string; if (!expr || expr->type != EXPR_STRING) { @@ -3431,58 +3527,41 @@ static void evaluate_asm_statement(struct statement *stmt) return; } - state = 0; - FOR_EACH_PTR(stmt->asm_outputs, expr) { - switch (state) { - case 0: /* Identifier */ - state = 1; - continue; + FOR_EACH_PTR(stmt->asm_outputs, op) { + /* Identifier */ - case 1: /* Constraint */ - state = 2; - if (!expr || expr->type != EXPR_STRING) { - sparse_error(expr ? expr->pos : stmt->pos, "asm output constraint is not a string"); - *THIS_ADDRESS(expr) = NULL; - continue; - } + /* Constraint */ + expr = op->constraint; + if (!expr || expr->type != EXPR_STRING) { + sparse_error(expr ? expr->pos : stmt->pos, "asm output constraint is not a string"); + op->constraint = NULL; + } else verify_output_constraint(expr, expr->string->data); - continue; - - case 2: /* Expression */ - state = 0; - if (!evaluate_expression(expr)) - return; - if (!lvalue_expression(expr)) - warning(expr->pos, "asm output is not an lvalue"); - evaluate_assign_to(expr, expr->ctype); - continue; - } - } END_FOR_EACH_PTR(expr); - - state = 0; - FOR_EACH_PTR(stmt->asm_inputs, expr) { - switch (state) { - case 0: /* Identifier */ - state = 1; - continue; - case 1: /* Constraint */ - state = 2; - if (!expr || expr->type != EXPR_STRING) { - sparse_error(expr ? expr->pos : stmt->pos, "asm input constraint is not a string"); - *THIS_ADDRESS(expr) = NULL; - continue; - } + /* Expression */ + expr = op->expr; + if (!evaluate_expression(expr)) + return; + if (!lvalue_expression(expr)) + warning(expr->pos, "asm output is not an lvalue"); + evaluate_assign_to(expr, expr->ctype); + } END_FOR_EACH_PTR(op); + + FOR_EACH_PTR(stmt->asm_inputs, op) { + /* Identifier */ + + /* Constraint */ + expr = op->constraint; + if (!expr || expr->type != EXPR_STRING) { + sparse_error(expr ? expr->pos : stmt->pos, "asm input constraint is not a string"); + op->constraint = NULL; + } else verify_input_constraint(expr, expr->string->data); - continue; - case 2: /* Expression */ - state = 0; - if (!evaluate_expression(expr)) - return; - continue; - } - } END_FOR_EACH_PTR(expr); + /* Expression */ + if (!evaluate_expression(op->expr)) + return; + } END_FOR_EACH_PTR(op); FOR_EACH_PTR(stmt->asm_clobbers, expr) { if (!expr) { @@ -3582,7 +3661,7 @@ static void evaluate_goto_statement(struct statement *stmt) { struct symbol *label = stmt->goto_label; - if (label && !label->stmt && !lookup_keyword(label->ident, NS_KEYWORD)) + if (label && !label->stmt && label->ident && !lookup_keyword(label->ident, NS_KEYWORD)) sparse_error(stmt->pos, "label '%s' was not declared", show_ident(label->ident)); evaluate_expression(stmt->goto_expression); diff --git a/usr/src/tools/smatch/src/evaluate.h b/usr/src/tools/smatch/src/evaluate.h new file mode 100644 index 0000000000..f68f7fb7c3 --- /dev/null +++ b/usr/src/tools/smatch/src/evaluate.h @@ -0,0 +1,28 @@ +#ifndef EVALUATE_H +#define EVALUATE_H + +struct expression; +struct statement; +struct symbol; +struct symbol_list; + +/// +// evaluate the type of an expression +// @expr: the expression to be evaluated +// @return: the type of the expression or ``NULL`` +// if the expression can't be evaluated +struct symbol *evaluate_expression(struct expression *expr); + +/// +// evaluate the type of a statement +// @stmt: the statement to be evaluated +// @return: the type of the statement or ``NULL`` +// if it can't be evaluated +struct symbol *evaluate_statement(struct statement *stmt); + +/// +// evaluate the type of a set of symbols +// @list: the list of the symbol to be evaluated +void evaluate_symbol_list(struct symbol_list *list); + +#endif diff --git a/usr/src/tools/smatch/src/example.c b/usr/src/tools/smatch/src/example.c index 691e0f97cb..8a2b1ab46f 100644 --- a/usr/src/tools/smatch/src/example.c +++ b/usr/src/tools/smatch/src/example.c @@ -25,15 +25,12 @@ static const char *opcodes[] = { [OP_BR] = "br", [OP_CBR] = "cbr", [OP_SWITCH] = "switch", - [OP_INVOKE] = "invoke", [OP_COMPUTEDGOTO] = "jmp *", - [OP_UNWIND] = "unwind", /* Binary */ [OP_ADD] = "add", [OP_SUB] = "sub", - [OP_MULU] = "mulu", - [OP_MULS] = "muls", + [OP_MUL] = "mul", [OP_DIVU] = "divu", [OP_DIVS] = "divs", [OP_MODU] = "modu", @@ -46,8 +43,6 @@ static const char *opcodes[] = { [OP_AND] = "and", [OP_OR] = "or", [OP_XOR] = "xor", - [OP_AND_BOOL] = "and-bool", - [OP_OR_BOOL] = "or-bool", /* Binary comparison */ [OP_SET_EQ] = "seteq", @@ -69,28 +64,27 @@ static const char *opcodes[] = { [OP_SEL] = "select", /* Memory */ - [OP_MALLOC] = "malloc", - [OP_FREE] = "free", - [OP_ALLOCA] = "alloca", [OP_LOAD] = "load", [OP_STORE] = "store", [OP_SETVAL] = "set", - [OP_GET_ELEMENT_PTR] = "getelem", /* Other */ [OP_PHI] = "phi", [OP_PHISOURCE] = "phisrc", [OP_COPY] = "copy", - [OP_CAST] = "cast", - [OP_SCAST] = "scast", - [OP_FPCAST] = "fpcast", + [OP_SEXT] = "sext", + [OP_ZEXT] = "zext", + [OP_TRUNC] = "trunc", + [OP_FCVTU] = "fcvtu", + [OP_FCVTS] = "fcvts", + [OP_UCVTF] = "ucvtf", + [OP_SCVTF] = "scvtf", + [OP_FCVTF] = "fcvtf", + [OP_UTPTR] = "utptr", + [OP_PTRTU] = "utptr", [OP_PTRCAST] = "ptrcast", [OP_CALL] = "call", - [OP_VANEXT] = "va_next", - [OP_VAARG] = "va_arg", [OP_SLICE] = "slice", - [OP_SNOP] = "snop", - [OP_LNOP] = "lnop", [OP_NOP] = "nop", [OP_DEATHNOTE] = "dead", [OP_ASM] = "asm", @@ -394,7 +388,7 @@ static void flush_reg(struct bb_state *state, struct hardreg *reg) return; reg->dead = 0; reg->used = 1; - FOR_EACH_PTR(reg->contains, pseudo) { + FOR_EACH_PTR_TAG(reg->contains, pseudo) { if (CURRENT_TAG(pseudo) & TAG_DEAD) continue; if (!(CURRENT_TAG(pseudo) & TAG_DIRTY)) @@ -447,7 +441,7 @@ static void mark_reg_dead(struct bb_state *state, pseudo_t pseudo, struct hardre { pseudo_t p; - FOR_EACH_PTR(reg->contains, p) { + FOR_EACH_PTR_TAG(reg->contains, p) { if (p != pseudo) continue; if (CURRENT_TAG(p) & TAG_DEAD) @@ -532,7 +526,7 @@ static struct hardreg *find_in_reg(struct bb_state *state, pseudo_t pseudo) pseudo_t p; reg = hardregs + i; - FOR_EACH_PTR(reg->contains, p) { + FOR_EACH_PTR_TAG(reg->contains, p) { if (p == pseudo) { last_reg = i; output_comment(state, "found pseudo %s in reg %s (busy=%d)", show_pseudo(pseudo), reg->name, reg->busy); @@ -872,7 +866,7 @@ static void kill_dead_reg(struct hardreg *reg) if (reg->dead) { pseudo_t p; - FOR_EACH_PTR(reg->contains, p) { + FOR_EACH_PTR_TAG(reg->contains, p) { if (CURRENT_TAG(p) & TAG_DEAD) { DELETE_CURRENT_PTR(p); reg->dead--; @@ -912,7 +906,7 @@ static void generate_binop(struct bb_state *state, struct instruction *insn) static int is_dead_reg(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg) { pseudo_t p; - FOR_EACH_PTR(reg->contains, p) { + FOR_EACH_PTR_TAG(reg->contains, p) { if (p == pseudo) return CURRENT_TAG(p) & TAG_DEAD; } END_FOR_EACH_PTR(p); @@ -1007,7 +1001,7 @@ static void kill_pseudo(struct bb_state *state, pseudo_t pseudo) pseudo_t p; reg = hardregs + i; - FOR_EACH_PTR(reg->contains, p) { + FOR_EACH_PTR_TAG(reg->contains, p) { if (p != pseudo) continue; if (CURRENT_TAG(p) & TAG_DEAD) @@ -1404,9 +1398,8 @@ static void generate_one_insn(struct instruction *insn, struct bb_state *state) generate_copy(state, insn); break; - case OP_ADD: case OP_MULU: case OP_MULS: + case OP_ADD: case OP_MUL: case OP_AND: case OP_OR: case OP_XOR: - case OP_AND_BOOL: case OP_OR_BOOL: generate_commutative_binop(state, insn); break; @@ -1420,7 +1413,14 @@ static void generate_one_insn(struct instruction *insn, struct bb_state *state) generate_compare(state, insn); break; - case OP_CAST: case OP_SCAST: case OP_FPCAST: case OP_PTRCAST: + case OP_SEXT: case OP_ZEXT: + case OP_TRUNC: + case OP_PTRCAST: + case OP_UTPTR: + case OP_PTRTU: + case OP_FCVTU: case OP_FCVTS: + case OP_UCVTF: case OP_SCVTF: + case OP_FCVTF: generate_cast(state, insn); break; @@ -1544,7 +1544,7 @@ static void fill_output(struct bb_state *state, pseudo_t pseudo, struct storage struct hardreg *reg = hardregs + i; pseudo_t p; - FOR_EACH_PTR(reg->contains, p) { + FOR_EACH_PTR_TAG(reg->contains, p) { if (p == pseudo) { write_reg_to_storage(state, reg, pseudo, out); return; @@ -1652,7 +1652,7 @@ static void generate_output_storage(struct bb_state *state) int flushme = 0; reg->busy = REG_FIXED; - FOR_EACH_PTR(reg->contains, p) { + FOR_EACH_PTR_TAG(reg->contains, p) { if (p == entry->pseudo) { flushme = -100; continue; @@ -1949,9 +1949,9 @@ int main(int argc, char **argv) compile(sparse_initialize(argc, argv, &filelist)); dbg_dead = 1; - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { compile(sparse(file)); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); return 0; } diff --git a/usr/src/tools/smatch/src/expand.c b/usr/src/tools/smatch/src/expand.c index 4113e83cb6..ecb45e7a81 100644 --- a/usr/src/tools/smatch/src/expand.c +++ b/usr/src/tools/smatch/src/expand.c @@ -41,11 +41,17 @@ #include "symbol.h" #include "target.h" #include "expression.h" +#include "evaluate.h" #include "expand.h" static int expand_expression(struct expression *); static int expand_statement(struct statement *); + +// If set, don't issue a warning on divide-by-0, invalid shift, ... +// and don't mark the expression as erroneous but leave it as-is. +// This allows testing some characteristics of the expression +// without creating any side-effects (e.g.: is_zero_constant()). static int conservative; static int expand_symbol_expression(struct expression *expr) @@ -157,11 +163,33 @@ Float: expr->type = EXPR_FVALUE; } -static int check_shift_count(struct expression *expr, struct symbol *ctype, unsigned int count) +static void warn_shift_count(struct expression *expr, struct symbol *ctype, long long count) { - warning(expr->pos, "shift too big (%u) for type %s", count, show_typename(ctype)); - count &= ctype->bit_size-1; - return count; + if (count < 0) { + if (!Wshift_count_negative) + return; + warning(expr->pos, "shift count is negative (%lld)", count); + return; + } + if (ctype->type == SYM_NODE) + ctype = ctype->ctype.base_type; + + if (!Wshift_count_overflow) + return; + warning(expr->pos, "shift too big (%llu) for type %s", count, show_typename(ctype)); +} + +/* Return true if constant shift size is valid */ +static bool check_shift_count(struct expression *expr, struct expression *right) +{ + struct symbol *ctype = expr->ctype; + long long count = get_longlong(right); + + if (count >= 0 && count < ctype->bit_size) + return true; + if (!conservative) + warn_shift_count(expr, ctype, count); + return false; } /* @@ -182,12 +210,8 @@ static int simplify_int_binop(struct expression *expr, struct symbol *ctype) return 0; r = right->value; if (expr->op == SPECIAL_LEFTSHIFT || expr->op == SPECIAL_RIGHTSHIFT) { - if (r >= ctype->bit_size) { - if (conservative) - return 0; - r = check_shift_count(expr, ctype, r); - right->value = r; - } + if (!check_shift_count(expr, right)) + return 0; } if (left->type != EXPR_VALUE) return 0; @@ -478,7 +502,7 @@ static int expand_comma(struct expression *expr) return cost; } -#define MOD_IGN (MOD_VOLATILE | MOD_CONST) +#define MOD_IGN (MOD_QUALIFIER) static int compare_types(int op, struct symbol *left, struct symbol *right) { @@ -529,27 +553,27 @@ static int expand_compare(struct expression *expr) static int expand_conditional(struct expression *expr) { struct expression *cond = expr->conditional; - struct expression *true = expr->cond_true; - struct expression *false = expr->cond_false; + struct expression *valt = expr->cond_true; + struct expression *valf = expr->cond_false; int cost, cond_cost; cond_cost = expand_expression(cond); if (cond->type == EXPR_VALUE) { unsigned flags = expr->flags; if (!cond->value) - true = false; - if (!true) - true = cond; - cost = expand_expression(true); - *expr = *true; + valt = valf; + if (!valt) + valt = cond; + cost = expand_expression(valt); + *expr = *valt; expr->flags = flags; if (expr->type == EXPR_VALUE) expr->taint |= cond->taint; return cost; } - cost = expand_expression(true); - cost += expand_expression(false); + cost = expand_expression(valt); + cost += expand_expression(valf); if (cost < SELECT_COST) { expr->type = EXPR_SELECT; @@ -558,11 +582,30 @@ static int expand_conditional(struct expression *expr) return cost + cond_cost + BRANCH_COST; } - + +static void check_assignment(struct expression *expr) +{ + struct expression *right; + + switch (expr->op) { + case SPECIAL_SHL_ASSIGN: + case SPECIAL_SHR_ASSIGN: + right = expr->right; + if (right->type != EXPR_VALUE) + break; + check_shift_count(expr, right); + break; + } + return; +} + static int expand_assignment(struct expression *expr) { expand_expression(expr->left); expand_expression(expr->right); + + if (!conservative) + check_assignment(expr); return SIDE_EFFECTS; } @@ -582,7 +625,7 @@ static struct expression *constant_symbol_value(struct symbol *sym, int offset) { struct expression *value; - if (sym->ctype.modifiers & (MOD_ASSIGNED | MOD_ADDRESSABLE)) + if (sym->ctype.modifiers & MOD_ACCESS) return NULL; value = sym->initializer; if (!value) @@ -644,6 +687,8 @@ static int expand_dereference(struct expression *expr) if (value) { /* FIXME! We should check that the size is right! */ if (value->type == EXPR_VALUE) { + if (is_bitfield_type(value->ctype)) + return UNSAFE; expr->type = EXPR_VALUE; expr->value = value->value; expr->taint = 0; @@ -788,6 +833,8 @@ static int expand_symbol_call(struct expression *expr, int cost) struct expression *fn = expr->fn; struct symbol *ctype = fn->ctype; + expand_expression(fn); + if (fn->type != EXPR_PREOP) return SIDE_EFFECTS; @@ -1048,6 +1095,9 @@ static int expand_expression(struct expression *expr) case EXPR_OFFSETOF: expression_error(expr, "internal front-end error: sizeof in expansion?"); return UNSAFE; + case EXPR_ASM_OPERAND: + expression_error(expr, "internal front-end error: ASM_OPERAND in expansion?"); + return UNSAFE; } return SIDE_EFFECTS; } @@ -1248,10 +1298,12 @@ static long long __get_expression_value(struct expression *expr, int strict) expression_error(expr, "bad constant expression"); return 0; } +#if 0 // This complains about "1 ? 1 :__bits_per()" which the kernel use if ((strict == 1) && bad_integer_constant_expression(expr)) { expression_error(expr, "bad integer constant expression"); return 0; } +#endif value = expr->value; mask = 1ULL << (ctype->bit_size-1); diff --git a/usr/src/tools/smatch/src/expression.c b/usr/src/tools/smatch/src/expression.c index cdd5e25170..91d043cde9 100644 --- a/usr/src/tools/smatch/src/expression.c +++ b/usr/src/tools/smatch/src/expression.c @@ -62,7 +62,10 @@ static struct token *comma_expression(struct token *, struct expression **); struct token *parens_expression(struct token *token, struct expression **expr, const char *where) { + struct token *p; + token = expect(token, '(', where); + p = token; if (match_op(token, '{')) { struct expression *e = alloc_expression(token->pos, EXPR_STATEMENT); struct statement *stmt = alloc_statement(token->pos, STMT_COMPOUND); @@ -74,6 +77,9 @@ struct token *parens_expression(struct token *token, struct expression **expr, c token = expect(token, '}', "at end of statement expression"); } else token = parse_expression(token, expr); + + if (token == p) + sparse_error(token->pos, "an expression is expected before ')'"); return expect(token, ')', where); } diff --git a/usr/src/tools/smatch/src/expression.h b/usr/src/tools/smatch/src/expression.h index 214313786d..f90f8d65ef 100644 --- a/usr/src/tools/smatch/src/expression.h +++ b/usr/src/tools/smatch/src/expression.h @@ -64,6 +64,7 @@ enum expression_type { EXPR_FVALUE, EXPR_SLICE, EXPR_OFFSETOF, + EXPR_ASM_OPERAND, }; @@ -194,7 +195,8 @@ struct expression { struct expression *base; unsigned r_bitpos, r_nrbits; }; - // EXPR_CAST and EXPR_SIZEOF + // EXPR_CAST, EXPR_FORCE_CAST, EXPR_IMPLIED_CAST, + // EXPR_SIZEOF, EXPR_ALIGNOF and EXPR_PTRSIZEOF struct /* cast_arg */ { struct symbol *cast_type; struct expression *cast_expression; @@ -241,12 +243,32 @@ struct expression { struct expression *index; }; }; + // EXPR_ASM_OPERAND + struct { + struct ident *name; + struct expression *constraint; + struct expression *expr; + }; }; }; -/* Constant expression values */ -int is_zero_constant(struct expression *); +/// +// Constant expression values +// -------------------------- + +/// +// test if an expression evaluates to the constant ``0``. +// @return: ``1`` if @expr evaluate to ``0``, +// ``0`` otherwise. +int is_zero_constant(struct expression *expr); + +/// +// test the compile time truth value of an expression +// @return: +// * ``-1`` if @expr is not constant, +// * ``0`` or ``1`` depending on the truth value of @expr. int expr_truth_value(struct expression *expr); + long long get_expression_value(struct expression *); long long const_expression_value(struct expression *); long long get_expression_value_silent(struct expression *expr); diff --git a/usr/src/tools/smatch/src/flow.c b/usr/src/tools/smatch/src/flow.c index fa5d31c8b2..ef8d04e582 100644 --- a/usr/src/tools/smatch/src/flow.c +++ b/usr/src/tools/smatch/src/flow.c @@ -131,7 +131,7 @@ static int try_to_simplify_bb(struct basic_block *bb, struct instruction *first, struct basic_block *source, *target; pseudo_t pseudo; struct instruction *br; - int true; + int cond; if (!def) continue; @@ -144,10 +144,10 @@ static int try_to_simplify_bb(struct basic_block *bb, struct instruction *first, continue; if (br->opcode != OP_CBR && br->opcode != OP_BR) continue; - true = pseudo_truth_value(pseudo); - if (true < 0) + cond = pseudo_truth_value(pseudo); + if (cond < 0) continue; - target = true ? second->bb_true : second->bb_false; + target = cond ? second->bb_true : second->bb_false; if (bb_depends_on(target, bb)) continue; if (bb_depends_on_phi(target, bb)) @@ -164,11 +164,20 @@ static int bb_has_side_effects(struct basic_block *bb) { struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { + if (!insn->bb) + continue; switch (insn->opcode) { case OP_CALL: /* FIXME! This should take "const" etc into account */ return 1; + case OP_LOAD: + if (!insn->type) + return 1; + if (insn->is_volatile) + return 1; + continue; + case OP_STORE: case OP_CONTEXT: return 1; @@ -200,7 +209,7 @@ static int simplify_phi_branch(struct basic_block *bb, struct instruction *br) } static int simplify_branch_branch(struct basic_block *bb, struct instruction *br, - struct basic_block **target_p, int true) + struct basic_block **target_p, int bb_true) { struct basic_block *target = *target_p, *final; struct instruction *insn; @@ -216,7 +225,7 @@ static int simplify_branch_branch(struct basic_block *bb, struct instruction *br * Now we just need to see if we can rewrite the branch.. */ retval = 0; - final = true ? insn->bb_true : insn->bb_false; + final = bb_true ? insn->bb_true : insn->bb_false; if (bb_has_side_effects(target)) goto try_to_rewrite_target; if (bb_depends_on(final, target)) @@ -269,7 +278,7 @@ int simplify_flow(struct entrypoint *ep) static inline void concat_user_list(struct pseudo_user_list *src, struct pseudo_user_list **dst) { - concat_ptr_list((struct ptr_list *)src, (struct ptr_list **)dst); + copy_ptr_list((struct ptr_list **)dst, (struct ptr_list *)src); } void convert_instruction_target(struct instruction *insn, pseudo_t src) @@ -296,9 +305,8 @@ void convert_instruction_target(struct instruction *insn, pseudo_t src) void convert_load_instruction(struct instruction *insn, pseudo_t src) { convert_instruction_target(insn, src); - /* Turn the load into a no-op */ - insn->opcode = OP_LNOP; - insn->bb = NULL; + kill_instruction(insn); + repeat_phase |= REPEAT_SYMBOL_CLEANUP; } static int overlapping_memop(struct instruction *a, struct instruction *b) @@ -362,66 +370,6 @@ int dominates(pseudo_t pseudo, struct instruction *insn, struct instruction *dom return 1; } -static int phisrc_in_bb(struct pseudo_list *list, struct basic_block *bb) -{ - pseudo_t p; - FOR_EACH_PTR(list, p) { - if (p->def->bb == bb) - return 1; - } END_FOR_EACH_PTR(p); - - return 0; -} - -static int find_dominating_parents(pseudo_t pseudo, struct instruction *insn, - struct basic_block *bb, unsigned long generation, struct pseudo_list **dominators, - int local) -{ - struct basic_block *parent; - - if (!bb->parents) - return !!local; - - FOR_EACH_PTR(bb->parents, parent) { - struct instruction *one; - struct instruction *br; - pseudo_t phi; - - FOR_EACH_PTR_REVERSE(parent->insns, one) { - int dominance; - if (one == insn) - goto no_dominance; - dominance = dominates(pseudo, insn, one, local); - if (dominance < 0) { - if (one->opcode == OP_LOAD) - continue; - return 0; - } - if (!dominance) - continue; - goto found_dominator; - } END_FOR_EACH_PTR_REVERSE(one); -no_dominance: - if (parent->generation == generation) - continue; - parent->generation = generation; - - if (!find_dominating_parents(pseudo, insn, parent, generation, dominators, local)) - return 0; - continue; - -found_dominator: - if (dominators && phisrc_in_bb(*dominators, parent)) - continue; - br = delete_last_instruction(&parent->insns); - phi = alloc_phi(parent, one->target, one->size); - phi->ident = phi->ident ? : pseudo->ident; - add_instruction(&parent->insns, br); - use_pseudo(insn, phi, add_pseudo(dominators, phi)); - } END_FOR_EACH_PTR(parent); - return 1; -} - /* * We should probably sort the phi list just to make it easier to compare * later for equality. @@ -434,9 +382,9 @@ void rewrite_load_instruction(struct instruction *insn, struct pseudo_list *domi * Check for somewhat common case of duplicate * phi nodes. */ - new = first_pseudo(dominators)->def->src1; + new = first_pseudo(dominators)->def->phi_src; FOR_EACH_PTR(dominators, phi) { - if (new != phi->def->src1) + if (new != phi->def->phi_src) goto complex_phi; new->ident = new->ident ? : phi->ident; } END_FOR_EACH_PTR(phi); @@ -446,11 +394,11 @@ void rewrite_load_instruction(struct instruction *insn, struct pseudo_list *domi * and convert the load into a LNOP and replace the * pseudo. */ + convert_load_instruction(insn, new); FOR_EACH_PTR(dominators, phi) { kill_instruction(phi->def); } END_FOR_EACH_PTR(phi); - convert_load_instruction(insn, new); - return; + goto end; complex_phi: /* We leave symbol pseudos with a bogus usage list here */ @@ -458,90 +406,27 @@ complex_phi: kill_use(&insn->src); insn->opcode = OP_PHI; insn->phi_list = dominators; -} - -static int find_dominating_stores(pseudo_t pseudo, struct instruction *insn, - unsigned long generation, int local) -{ - struct basic_block *bb = insn->bb; - struct instruction *one, *dom = NULL; - struct pseudo_list *dominators; - int partial; - - /* Unreachable load? Undo it */ - if (!bb) { - insn->opcode = OP_LNOP; - return 1; - } - partial = 0; - FOR_EACH_PTR(bb->insns, one) { - int dominance; - if (one == insn) - goto found; - dominance = dominates(pseudo, insn, one, local); - if (dominance < 0) { - /* Ignore partial load dominators */ - if (one->opcode == OP_LOAD) - continue; - dom = NULL; - partial = 1; - continue; - } - if (!dominance) - continue; - dom = one; - partial = 0; - } END_FOR_EACH_PTR(one); - /* Whaa? */ - warning(pseudo->sym->pos, "unable to find symbol read"); - return 0; -found: - if (partial) - return 0; - - if (dom) { - convert_load_instruction(insn, dom->target); - return 1; - } - - /* OK, go find the parents */ - bb->generation = generation; - - dominators = NULL; - if (!find_dominating_parents(pseudo, insn, bb, generation, &dominators, local)) - return 0; - - /* This happens with initial assignments to structures etc.. */ - if (!dominators) { - if (!local) - return 0; - check_access(insn); - convert_load_instruction(insn, value_pseudo(insn->type, 0)); - return 1; - } - - /* - * If we find just one dominating instruction, we - * can turn it into a direct thing. Otherwise we'll - * have to turn the load into a phi-node of the - * dominators. - */ - rewrite_load_instruction(insn, dominators); - return 1; -} - -static void kill_store(struct instruction *insn) -{ - if (insn) { - insn->bb = NULL; - insn->opcode = OP_SNOP; - kill_use(&insn->target); - } +end: + repeat_phase |= REPEAT_SYMBOL_CLEANUP; } /* Kill a pseudo that is dead on exit from the bb */ -static void kill_dead_stores(pseudo_t pseudo, unsigned long generation, struct basic_block *bb, int local) +// The context is: +// * the variable is not global but may have its address used (local/non-local) +// * the stores are only needed by others functions which would do some +// loads via the escaped address +// We start by the terminating BB (normal exit BB + no-return/unreachable) +// We walkup the BB' intruction backward +// * we're only concerned by loads, stores & calls +// * if we reach a call -> we have to stop if var is non-local +// * if we reach a load of our var -> we have to stop +// * if we reach a store of our var -> we can kill it, it's dead +// * we can ignore other stores & loads if the var is local +// * if we reach another store or load done via non-symbol access +// (so done via some address calculation) -> we have to stop +// If we reach the top of the BB we can recurse into the parents BBs. +static void kill_dead_stores_bb(pseudo_t pseudo, unsigned long generation, struct basic_block *bb, int local) { struct instruction *insn; struct basic_block *parent; @@ -550,85 +435,33 @@ static void kill_dead_stores(pseudo_t pseudo, unsigned long generation, struct b return; bb->generation = generation; FOR_EACH_PTR_REVERSE(bb->insns, insn) { - int opcode = insn->opcode; - - if (opcode != OP_LOAD && opcode != OP_STORE) { - if (local) - continue; - if (opcode == OP_CALL) - return; + if (!insn->bb) continue; - } - if (insn->src == pseudo) { - if (opcode == OP_LOAD) + switch (insn->opcode) { + case OP_LOAD: + if (insn->src == pseudo) return; - kill_store(insn); + break; + case OP_STORE: + if (insn->src == pseudo) { + kill_instruction_force(insn); + continue; + } + break; + case OP_CALL: + if (!local) + return; + default: continue; } - if (local) - continue; - if (insn->src->type != PSEUDO_SYM) + if (!local && insn->src->type != PSEUDO_SYM) return; } END_FOR_EACH_PTR_REVERSE(insn); FOR_EACH_PTR(bb->parents, parent) { - struct basic_block *child; - FOR_EACH_PTR(parent->children, child) { - if (child && child != bb) - return; - } END_FOR_EACH_PTR(child); - kill_dead_stores(pseudo, generation, parent, local); - } END_FOR_EACH_PTR(parent); -} - -/* - * This should see if the "insn" trivially dominates some previous store, and kill the - * store if unnecessary. - */ -static void kill_dominated_stores(pseudo_t pseudo, struct instruction *insn, - unsigned long generation, struct basic_block *bb, int local, int found) -{ - struct instruction *one; - struct basic_block *parent; - - /* Unreachable store? Undo it */ - if (!bb) { - kill_store(insn); - return; - } - if (bb->generation == generation) - return; - bb->generation = generation; - FOR_EACH_PTR_REVERSE(bb->insns, one) { - int dominance; - if (!found) { - if (one != insn) - continue; - found = 1; + if (bb_list_size(parent->children) > 1) continue; - } - dominance = dominates(pseudo, insn, one, local); - if (!dominance) - continue; - if (dominance < 0) - return; - if (one->opcode == OP_LOAD) - return; - kill_store(one); - } END_FOR_EACH_PTR_REVERSE(one); - - if (!found) { - warning(bb->pos, "Unable to find instruction"); - return; - } - - FOR_EACH_PTR(bb->parents, parent) { - struct basic_block *child; - FOR_EACH_PTR(parent->children, child) { - if (child && child != bb) - return; - } END_FOR_EACH_PTR(child); - kill_dominated_stores(pseudo, insn, generation, parent, local, found); + kill_dead_stores_bb(pseudo, generation, parent, local); } END_FOR_EACH_PTR(parent); } @@ -640,104 +473,56 @@ void check_access(struct instruction *insn) int offset = insn->offset, bit = bytes_to_bits(offset) + insn->size; struct symbol *sym = pseudo->sym; - if (sym->bit_size > 0 && (offset < 0 || bit > sym->bit_size)) + if (sym->bit_size > 0 && (offset < 0 || bit > sym->bit_size)) { + if (insn->tainted) + return; warning(insn->pos, "invalid access %s '%s' (%d %d)", offset < 0 ? "below" : "past the end of", show_ident(sym->ident), offset, bits_to_bytes(sym->bit_size)); + insn->tainted = 1; + } } } -static void simplify_one_symbol(struct entrypoint *ep, struct symbol *sym) +static struct pseudo_user *first_user(pseudo_t p) { - pseudo_t pseudo; struct pseudo_user *pu; - unsigned long mod; - int all; - - /* Never used as a symbol? */ - pseudo = sym->pseudo; - if (!pseudo) - return; - - /* We don't do coverage analysis of volatiles.. */ - if (sym->ctype.modifiers & MOD_VOLATILE) - return; - - /* ..and symbols with external visibility need more care */ - mod = sym->ctype.modifiers & (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE); - if (mod) - goto external_visibility; - - FOR_EACH_PTR(pseudo->users, pu) { - /* We know that the symbol-pseudo use is the "src" in the instruction */ - struct instruction *insn = pu->insn; - - switch (insn->opcode) { - case OP_STORE: - break; - case OP_LOAD: - break; - case OP_SYMADDR: - if (!insn->bb) - continue; - mod |= MOD_ADDRESSABLE; - goto external_visibility; - case OP_NOP: - case OP_SNOP: - case OP_LNOP: - case OP_PHI: + FOR_EACH_PTR(p->users, pu) { + if (!pu) continue; - default: - warning(sym->pos, "symbol '%s' pseudo used in unexpected way", show_ident(sym->ident)); - } + return pu; } END_FOR_EACH_PTR(pu); + return NULL; +} -external_visibility: - all = 1; - FOR_EACH_PTR_REVERSE(pseudo->users, pu) { - struct instruction *insn = pu->insn; - if (insn->opcode == OP_LOAD) - all &= find_dominating_stores(pseudo, insn, ++bb_generation, !mod); - } END_FOR_EACH_PTR_REVERSE(pu); - - /* If we converted all the loads, remove the stores. They are dead */ - if (all && !mod) { - FOR_EACH_PTR(pseudo->users, pu) { - struct instruction *insn = pu->insn; - if (insn->opcode == OP_STORE) - kill_store(insn); - } END_FOR_EACH_PTR(pu); - } else { - /* - * If we couldn't take the shortcut, see if we can at least kill some - * of them.. - */ - FOR_EACH_PTR(pseudo->users, pu) { +void kill_dead_stores(struct entrypoint *ep, pseudo_t addr, int local) +{ + unsigned long generation; + struct basic_block *bb; + + switch (pseudo_user_list_size(addr->users)) { + case 0: + return; + case 1: + if (local) { + struct pseudo_user *pu = first_user(addr); struct instruction *insn = pu->insn; - if (insn->opcode == OP_STORE) - kill_dominated_stores(pseudo, insn, ++bb_generation, insn->bb, !mod, 0); - } END_FOR_EACH_PTR(pu); - - if (!(mod & (MOD_NONLOCAL | MOD_STATIC))) { - struct basic_block *bb; - FOR_EACH_PTR(ep->bbs, bb) { - if (!bb->children) - kill_dead_stores(pseudo, ++bb_generation, bb, !mod); - } END_FOR_EACH_PTR(bb); + if (insn->opcode == OP_STORE) { + kill_instruction_force(insn); + return; + } } + default: + break; } - - return; -} - -void simplify_symbol_usage(struct entrypoint *ep) -{ - pseudo_t pseudo; - FOR_EACH_PTR(ep->accesses, pseudo) { - simplify_one_symbol(ep, pseudo->sym); - } END_FOR_EACH_PTR(pseudo); + generation = ++bb_generation; + FOR_EACH_PTR(ep->bbs, bb) { + if (bb->children) + continue; + kill_dead_stores_bb(addr, generation, bb, local); + } END_FOR_EACH_PTR(bb); } static void mark_bb_reachable(struct basic_block *bb, unsigned long generation) @@ -770,6 +555,8 @@ void kill_bb(struct basic_block *bb) struct basic_block *child, *parent; FOR_EACH_PTR(bb->insns, insn) { + if (!insn->bb) + continue; kill_instruction_force(insn); kill_defs(insn); /* @@ -844,13 +631,12 @@ static struct basic_block * rewrite_branch_bb(struct basic_block *bb, struct ins { struct basic_block *parent; struct basic_block *target = br->bb_true; - struct basic_block *false = br->bb_false; if (br->opcode == OP_CBR) { pseudo_t cond = br->cond; if (cond->type != PSEUDO_VAL) return NULL; - target = cond->value ? target : false; + target = cond->value ? target : br->bb_false; } /* @@ -956,7 +742,8 @@ void pack_basic_blocks(struct entrypoint *ep) if (!first->bb) continue; switch (first->opcode) { - case OP_NOP: case OP_LNOP: case OP_SNOP: + case OP_NOP: + case OP_INLINED_CALL: continue; case OP_CBR: case OP_BR: { @@ -1002,7 +789,7 @@ out: /* * Merge the two. */ - repeat_phase |= REPEAT_CSE; + repeat_phase |= REPEAT_CFG_CLEANUP; parent->children = bb->children; bb->children = NULL; @@ -1014,10 +801,10 @@ out: kill_instruction(delete_last_instruction(&parent->insns)); FOR_EACH_PTR(bb->insns, insn) { - if (insn->bb) { - assert(insn->bb == bb); - insn->bb = parent; - } + if (!insn->bb) + continue; + assert(insn->bb == bb); + insn->bb = parent; add_instruction(&parent->insns, insn); } END_FOR_EACH_PTR(insn); bb->insns = NULL; diff --git a/usr/src/tools/smatch/src/flow.h b/usr/src/tools/smatch/src/flow.h index b592ad4d3c..099767d408 100644 --- a/usr/src/tools/smatch/src/flow.h +++ b/usr/src/tools/smatch/src/flow.h @@ -5,35 +5,37 @@ extern unsigned long bb_generation; -#define REPEAT_CSE 1 -#define REPEAT_SYMBOL_CLEANUP 2 -#define REPEAT_CFG_CLEANUP 3 +#define REPEAT_CSE (1 << 0) +#define REPEAT_SYMBOL_CLEANUP (1 << 1) +#define REPEAT_CFG_CLEANUP (1 << 2) struct entrypoint; struct instruction; extern int simplify_flow(struct entrypoint *ep); +extern void kill_dead_stores(struct entrypoint *ep, pseudo_t addr, int local); extern void simplify_symbol_usage(struct entrypoint *ep); extern void simplify_memops(struct entrypoint *ep); extern void pack_basic_blocks(struct entrypoint *ep); extern void convert_instruction_target(struct instruction *insn, pseudo_t src); -extern void cleanup_and_cse(struct entrypoint *ep); +extern void remove_dead_insns(struct entrypoint *); extern int simplify_instruction(struct instruction *); extern void kill_bb(struct basic_block *); extern void kill_use(pseudo_t *); +extern void remove_use(pseudo_t *); extern void kill_unreachable_bbs(struct entrypoint *ep); -extern void kill_insn(struct instruction *, int force); -static inline void kill_instruction(struct instruction *insn) +extern int kill_insn(struct instruction *, int force); +static inline int kill_instruction(struct instruction *insn) { - kill_insn(insn, 0); + return kill_insn(insn, 0); } -static inline void kill_instruction_force(struct instruction *insn) +static inline int kill_instruction_force(struct instruction *insn) { - kill_insn(insn, 1); + return kill_insn(insn, 1); } void check_access(struct instruction *insn); @@ -41,11 +43,6 @@ void convert_load_instruction(struct instruction *, pseudo_t); void rewrite_load_instruction(struct instruction *, struct pseudo_list *); int dominates(pseudo_t pseudo, struct instruction *insn, struct instruction *dom, int local); -extern void clear_liveness(struct entrypoint *ep); -extern void track_pseudo_liveness(struct entrypoint *ep); -extern void track_pseudo_death(struct entrypoint *ep); -extern void track_phi_uses(struct instruction *insn); - extern void vrfy_flow(struct entrypoint *ep); extern int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo); diff --git a/usr/src/tools/smatch/src/flowgraph.c b/usr/src/tools/smatch/src/flowgraph.c new file mode 100644 index 0000000000..8fc22dcfdc --- /dev/null +++ b/usr/src/tools/smatch/src/flowgraph.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: MIT +// +// Various utilities for flowgraphs. +// +// Copyright (c) 2017 Luc Van Oostenryck. +// + +#include "flowgraph.h" +#include "linearize.h" +#include "flow.h" // for bb_generation +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + + +struct cfg_info { + struct basic_block_list *list; + unsigned long gen; + unsigned int nr; +}; + + +static void label_postorder(struct basic_block *bb, struct cfg_info *info) +{ + struct basic_block *child; + + if (bb->generation == info->gen) + return; + + bb->generation = info->gen; + FOR_EACH_PTR_REVERSE(bb->children, child) { + label_postorder(child, info); + } END_FOR_EACH_PTR_REVERSE(child); + + bb->postorder_nr = info->nr++; + add_bb(&info->list, bb); +} + +static void reverse_bbs(struct basic_block_list **dst, struct basic_block_list *src) +{ + struct basic_block *bb; + FOR_EACH_PTR_REVERSE(src, bb) { + add_bb(dst, bb); + } END_FOR_EACH_PTR_REVERSE(bb); +} + +static void debug_postorder(struct entrypoint *ep) +{ + struct basic_block *bb; + + printf("%s's reverse postorder:\n", show_ident(ep->name->ident)); + FOR_EACH_PTR(ep->bbs, bb) { + printf("\t.L%u: %u\n", bb->nr, bb->postorder_nr); + } END_FOR_EACH_PTR(bb); +} + +// +// cfg_postorder - Set the BB's reverse postorder links +// +// Do a postorder DFS walk and set the links +// (which will do the reverse part). +// +int cfg_postorder(struct entrypoint *ep) +{ + struct cfg_info info = { + .gen = ++bb_generation, + }; + + label_postorder(ep->entry->bb, &info); + + // OK, now info.list contains the node in postorder + // Reuse ep->bbs for the reverse postorder. + free_ptr_list(&ep->bbs); + ep->bbs = NULL; + reverse_bbs(&ep->bbs, info.list); + free_ptr_list(&info.list); + if (dbg_postorder) + debug_postorder(ep); + return info.nr; +} + +// +// Calculate the dominance tree following: +// "A simple, fast dominance algorithm" +// by K. D. Cooper, T. J. Harvey, and K. Kennedy. +// cfr. http://www.cs.rice.edu/∼keith/EMBED/dom.pdf +// +static struct basic_block *intersect_dom(struct basic_block *doms[], + struct basic_block *b1, struct basic_block *b2) +{ + int f1 = b1->postorder_nr, f2 = b2->postorder_nr; + while (f1 != f2) { + while (f1 < f2) { + b1 = doms[f1]; + f1 = b1->postorder_nr; + } + while (f2 < f1) { + b2 = doms[f2]; + f2 = b2->postorder_nr; + } + } + return b1; +} + +static void debug_domtree(struct entrypoint *ep) +{ + struct basic_block *bb = ep->entry->bb; + + printf("%s's idoms:\n", show_ident(ep->name->ident)); + FOR_EACH_PTR(ep->bbs, bb) { + if (bb == ep->entry->bb) + continue; // entry node has no idom + printf("\t%s <- %s\n", show_label(bb), show_label(bb->idom)); + } END_FOR_EACH_PTR(bb); +} + +void domtree_build(struct entrypoint *ep) +{ + struct basic_block *entry = ep->entry->bb; + struct basic_block **doms; + struct basic_block *bb; + unsigned int size; + int max_level = 0; + int changed; + + // First calculate the (reverse) postorder. + // This will give use us: + // - the links to do a reverse postorder traversal + // - the order number for each block + size = cfg_postorder(ep); + + // initialize the dominators array + doms = calloc(size, sizeof(*doms)); + assert(entry->postorder_nr == size-1); + doms[size-1] = entry; + + do { + struct basic_block *b; + + changed = 0; + FOR_EACH_PTR(ep->bbs, b) { + struct basic_block *p; + int bnr = b->postorder_nr; + struct basic_block *new_idom = NULL; + + if (b == entry) + continue; // ignore entry node + + FOR_EACH_PTR(b->parents, p) { + unsigned int pnr = p->postorder_nr; + if (!doms[pnr]) + continue; + if (!new_idom) { + new_idom = p; + continue; + } + + new_idom = intersect_dom(doms, p, new_idom); + } END_FOR_EACH_PTR(p); + + assert(new_idom); + if (doms[bnr] != new_idom) { + doms[bnr] = new_idom; + changed = 1; + } + } END_FOR_EACH_PTR(b); + } while (changed); + + // set the idom links + FOR_EACH_PTR(ep->bbs, bb) { + struct basic_block *idom = doms[bb->postorder_nr]; + + if (bb == entry) + continue; // ignore entry node + + bb->idom = idom; + add_bb(&idom->doms, bb); + } END_FOR_EACH_PTR(bb); + entry->idom = NULL; + + // set the dominance levels + FOR_EACH_PTR(ep->bbs, bb) { + struct basic_block *idom = bb->idom; + int level = idom ? idom->dom_level + 1 : 0; + + bb->dom_level = level; + if (max_level < level) + max_level = level; + } END_FOR_EACH_PTR(bb); + ep->dom_levels = max_level + 1; + + free(doms); + if (dbg_domtree) + debug_domtree(ep); +} + +// dt_dominates - does BB a dominates BB b? +bool domtree_dominates(struct basic_block *a, struct basic_block *b) +{ + if (a == b) // dominance is reflexive + return true; + if (a == b->idom) + return true; + if (b == a->idom) + return false; + + // can't dominate if deeper in the DT + if (a->dom_level >= b->dom_level) + return false; + + // FIXME: can be faster if we have the DFS in-out numbers + + // walk up the dominator tree + for (b = b->idom; b; b = b->idom) { + if (b == a) + return true; + } + return false; +} diff --git a/usr/src/tools/smatch/src/flowgraph.h b/usr/src/tools/smatch/src/flowgraph.h new file mode 100644 index 0000000000..7226c55f08 --- /dev/null +++ b/usr/src/tools/smatch/src/flowgraph.h @@ -0,0 +1,13 @@ +#ifndef FLOWGRAPH_H +#define FLOWGRAPH_H + +#include <stdbool.h> + +struct entrypoint; +struct basic_block; + +int cfg_postorder(struct entrypoint *ep); +void domtree_build(struct entrypoint *ep); +bool domtree_dominates(struct basic_block *a, struct basic_block *b); + +#endif diff --git a/usr/src/tools/smatch/src/gcc-attr-list.h b/usr/src/tools/smatch/src/gcc-attr-list.h index 9acb982fd7..c780017572 100644 --- a/usr/src/tools/smatch/src/gcc-attr-list.h +++ b/usr/src/tools/smatch/src/gcc-attr-list.h @@ -6,7 +6,6 @@ GCC_ATTR(abi_tag) GCC_ATTR(absdata) GCC_ATTR(address) GCC_ATTR(alias) -GCC_ATTR(aligned) GCC_ATTR(alloc_align) GCC_ATTR(alloc_size) GCC_ATTR(altivec) @@ -31,12 +30,9 @@ GCC_ATTR(cmse_nonsecure_entry) GCC_ATTR(cold) GCC_ATTR(common) GCC_ATTR(common_object) -GCC_ATTR(const) GCC_ATTR(constructor) GCC_ATTR(critical) -GCC_ATTR(default) GCC_ATTR(deprecated) -GCC_ATTR(designated_init) GCC_ATTR(destructor) GCC_ATTR(disinterrupt) GCC_ATTR(dllexport) @@ -46,8 +42,6 @@ GCC_ATTR(either) GCC_ATTR(error) GCC_ATTR(exception) GCC_ATTR(exception_handler) -GCC_ATTR(externally_visible) -GCC_ATTR(fallthrough) GCC_ATTR(far) GCC_ATTR(fast_interrupt) GCC_ATTR(fastcall) @@ -98,7 +92,6 @@ GCC_ATTR(maybe_unused) GCC_ATTR(medium_call) GCC_ATTR(micromips) GCC_ATTR(mips16) -GCC_ATTR(mode) GCC_ATTR(model) GCC_ATTR(monitor) GCC_ATTR(ms_abi) @@ -137,20 +130,17 @@ GCC_ATTR(nomips16) GCC_ATTR(nonnull) GCC_ATTR(nonstring) GCC_ATTR(noplt) -GCC_ATTR(noreturn) GCC_ATTR(nosave_low_regs) GCC_ATTR(not_nested) GCC_ATTR(nothrow) GCC_ATTR(notshared) GCC_ATTR(optimize) -GCC_ATTR(packed) GCC_ATTR(partial_save) GCC_ATTR(patchable_function_entry) GCC_ATTR(pcs) GCC_ATTR(persistent) GCC_ATTR(progmem) GCC_ATTR(protected) -GCC_ATTR(pure) GCC_ATTR(reentrant) GCC_ATTR(regparm) GCC_ATTR(renesas) @@ -195,7 +185,6 @@ GCC_ATTR(transaction_safe) GCC_ATTR(transaction_safe_dynamic) GCC_ATTR(transaction_unsafe) GCC_ATTR(transaction_wrap) -GCC_ATTR(transparent_union) GCC_ATTR(trap_exit) GCC_ATTR(trapa_handler) GCC_ATTR(uncached) diff --git a/usr/src/tools/smatch/src/gdbhelpers b/usr/src/tools/smatch/src/gdbhelpers index 86347863ae..2fe9336ddd 100644 --- a/usr/src/tools/smatch/src/gdbhelpers +++ b/usr/src/tools/smatch/src/gdbhelpers @@ -107,6 +107,12 @@ define gdb_show_ctype if ($arg0->modifiers & MOD_VOLATILE) printf "MOD_VOLATILE " end + if ($arg0->modifiers & MOD_RESTRICT) + printf "MOD_RESTRICT " + end + if ($arg0->modifiers & MOD_ATOMIC) + printf "MOD_ATOMIC " + end if ($arg0->modifiers & MOD_SIGNED) printf "MOD_SIGNED " end @@ -128,9 +134,6 @@ define gdb_show_ctype if ($arg0->modifiers & MOD_LONGLONGLONG) printf "MOD_LONGLONGLONG " end - if ($arg0->modifiers & MOD_TYPEDEF) - printf "MOD_TYPEDEF " - end if ($arg0->modifiers & MOD_INLINE) printf "MOD_INLINE " end @@ -143,9 +146,6 @@ define gdb_show_ctype if ($arg0->modifiers & MOD_NODEREF) printf "MOD_NODEREF " end - if ($arg0->modifiers & MOD_ACCESSED) - printf "MOD_ACCESSED " - end if ($arg0->modifiers & MOD_TOPLEVEL) printf "MOD_TOPLEVEL " end diff --git a/usr/src/tools/smatch/src/graph.c b/usr/src/tools/smatch/src/graph.c index 8cbc220273..be4cf282c0 100644 --- a/usr/src/tools/smatch/src/graph.c +++ b/usr/src/tools/smatch/src/graph.c @@ -74,17 +74,19 @@ static void graph_ep(struct entrypoint *ep) /* List loads and stores */ FOR_EACH_PTR(bb->insns, insn) { + if (!insn->bb) + continue; switch(insn->opcode) { case OP_STORE: - if (insn->symbol->type == PSEUDO_SYM) { - printf("%s store(%s)", s, show_ident(insn->symbol->sym->ident)); + if (insn->src->type == PSEUDO_SYM) { + printf("%s store(%s)", s, show_ident(insn->src->sym->ident)); s = ","; } break; case OP_LOAD: - if (insn->symbol->type == PSEUDO_SYM) { - printf("%s load(%s)", s, show_ident(insn->symbol->sym->ident)); + if (insn->src->type == PSEUDO_SYM) { + printf("%s load(%s)", s, show_ident(insn->src->sym->ident)); s = ","; } break; @@ -130,6 +132,8 @@ static void graph_calls(struct entrypoint *ep, int internal) continue; FOR_EACH_PTR(bb->insns, insn) { + if (!insn->bb) + continue; if (insn->opcode == OP_CALL && internal == !(insn->func->sym->ctype.modifiers & MOD_EXTERN)) { @@ -172,7 +176,7 @@ int main(int argc, char **argv) /* Linearize all symbols, graph internal basic block * structures and intra-file calls */ - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { fsyms = sparse(file); concat_symbol_list(fsyms, &all_syms); @@ -187,15 +191,15 @@ int main(int argc, char **argv) graph_ep(sym->ep); graph_calls(sym->ep, 1); } - } END_FOR_EACH_PTR_NOTAG(sym); + } END_FOR_EACH_PTR(sym); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); /* Graph inter-file calls */ FOR_EACH_PTR(all_syms, sym) { if (sym->ep) graph_calls(sym->ep, 0); - } END_FOR_EACH_PTR_NOTAG(sym); + } END_FOR_EACH_PTR(sym); printf("}\n"); return 0; diff --git a/usr/src/tools/smatch/src/ident-list.h b/usr/src/tools/smatch/src/ident-list.h index 3fb817e9cc..096b1c2358 100644 --- a/usr/src/tools/smatch/src/ident-list.h +++ b/usr/src/tools/smatch/src/ident-list.h @@ -37,7 +37,7 @@ IDENT_RESERVED(_Imaginary); /* C11 keywords */ IDENT(_Alignas); IDENT_RESERVED(_Alignof); -IDENT_RESERVED(_Atomic); +IDENT(_Atomic); IDENT_RESERVED(_Generic); IDENT(_Noreturn); IDENT_RESERVED(_Static_assert); @@ -59,17 +59,14 @@ IDENT_RESERVED(__label__); * sparse. */ IDENT(defined); IDENT(once); +IDENT(__has_attribute); +IDENT(__has_builtin); __IDENT(pragma_ident, "__pragma__", 0); __IDENT(_Pragma_ident, "_Pragma", 0); __IDENT(__VA_ARGS___ident, "__VA_ARGS__", 0); -__IDENT(__LINE___ident, "__LINE__", 0); -__IDENT(__FILE___ident, "__FILE__", 0); -__IDENT(__DATE___ident, "__DATE__", 0); -__IDENT(__TIME___ident, "__TIME__", 0); __IDENT(__func___ident, "__func__", 0); __IDENT(__FUNCTION___ident, "__FUNCTION__", 0); __IDENT(__PRETTY_FUNCTION___ident, "__PRETTY_FUNCTION__", 0); -__IDENT(__COUNTER___ident, "__COUNTER__", 0); /* Sparse commands */ IDENT_RESERVED(__context__); diff --git a/usr/src/tools/smatch/src/inline.c b/usr/src/tools/smatch/src/inline.c index a3002c6bda..fcc43db5c8 100644 --- a/usr/src/tools/smatch/src/inline.c +++ b/usr/src/tools/smatch/src/inline.c @@ -32,6 +32,9 @@ #include "parse.h" #include "symbol.h" #include "expression.h" +#include "evaluate.h" + +static void copy_statement(struct statement *src, struct statement *dst); static struct expression * dup_expression(struct expression *expr) { @@ -178,14 +181,14 @@ static struct expression * copy_expression(struct expression *expr) case EXPR_SELECT: case EXPR_CONDITIONAL: { struct expression *cond = copy_expression(expr->conditional); - struct expression *true = copy_expression(expr->cond_true); - struct expression *false = copy_expression(expr->cond_false); - if (cond == expr->conditional && true == expr->cond_true && false == expr->cond_false) + struct expression *valt = copy_expression(expr->cond_true); + struct expression *valf = copy_expression(expr->cond_false); + if (cond == expr->conditional && valt == expr->cond_true && valf == expr->cond_false) break; expr = dup_expression(expr); expr->conditional = cond; - expr->cond_true = true; - expr->cond_false = false; + expr->cond_true = valt; + expr->cond_false = valf; break; } @@ -271,6 +274,12 @@ static struct expression * copy_expression(struct expression *expr) } break; } + case EXPR_ASM_OPERAND: { + expr = dup_expression(expr); + expr->constraint = copy_expression(expr->constraint); + expr->expr = copy_expression(expr->expr); + break; + } default: warning(expr->pos, "trying to copy expression type %d", expr->type); } @@ -281,20 +290,9 @@ static struct expression_list *copy_asm_constraints(struct expression_list *in) { struct expression_list *out = NULL; struct expression *expr; - int state = 0; FOR_EACH_PTR(in, expr) { - switch (state) { - case 0: /* identifier */ - case 1: /* constraint */ - state++; - add_expression(&out, expr); - continue; - case 2: /* expression */ - state = 0; - add_expression(&out, copy_expression(expr)); - continue; - } + add_expression(&out, copy_expression(expr)); } END_FOR_EACH_PTR(expr); return out; } @@ -369,20 +367,20 @@ static struct statement *copy_one_statement(struct statement *stmt) } case STMT_IF: { struct expression *cond = stmt->if_conditional; - struct statement *true = stmt->if_true; - struct statement *false = stmt->if_false; + struct statement *valt = stmt->if_true; + struct statement *valf = stmt->if_false; cond = copy_expression(cond); - true = copy_one_statement(true); - false = copy_one_statement(false); + valt = copy_one_statement(valt); + valf = copy_one_statement(valf); if (stmt->if_conditional == cond && - stmt->if_true == true && - stmt->if_false == false) + stmt->if_true == valt && + stmt->if_false == valf) break; stmt = dup_statement(stmt); stmt->if_conditional = cond; - stmt->if_true = true; - stmt->if_false = false; + stmt->if_true = valt; + stmt->if_false = valf; break; } case STMT_RETURN: { @@ -468,7 +466,7 @@ static struct statement *copy_one_statement(struct statement *stmt) * This doesn't do the symbol replacement right: it's not * re-entrant. */ -void copy_statement(struct statement *src, struct statement *dst) +static void copy_statement(struct statement *src, struct statement *dst) { struct statement *stmt; diff --git a/usr/src/tools/smatch/src/ir.c b/usr/src/tools/smatch/src/ir.c new file mode 100644 index 0000000000..2e284c251e --- /dev/null +++ b/usr/src/tools/smatch/src/ir.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: MIT + +#include "ir.h" +#include "linearize.h" +#include <stdlib.h> +#include <assert.h> + + +static int nbr_phi_operands(struct instruction *insn) +{ + pseudo_t p; + int nbr = 0; + + if (!insn->phi_list) + return 0; + + FOR_EACH_PTR(insn->phi_list, p) { + if (p == VOID) + continue; + nbr++; + } END_FOR_EACH_PTR(p); + + return nbr; +} + +static int check_phi_node(struct instruction *insn) +{ + struct basic_block *par; + pseudo_t phi; + int err = 0; + + if (!has_users(insn->target)) + return err; + + if (bb_list_size(insn->bb->parents) != nbr_phi_operands(insn)) { + sparse_error(insn->pos, "bad number of phi operands in:\n\t%s", + show_instruction(insn)); + info(insn->pos, "parents: %d", bb_list_size(insn->bb->parents)); + info(insn->pos, "phisrcs: %d", nbr_phi_operands(insn)); + return 1; + } + + PREPARE_PTR_LIST(insn->bb->parents, par); + FOR_EACH_PTR(insn->phi_list, phi) { + struct instruction *src; + if (phi == VOID) + continue; + assert(phi->type == PSEUDO_PHI); + src = phi->def; + if (src->bb != par) { + sparse_error(src->pos, "wrong BB for %s:", show_instruction(src)); + info(src->pos, "expected: %s", show_label(par)); + info(src->pos, " got: %s", show_label(src->bb)); + err++; + } + NEXT_PTR_LIST(par); + } END_FOR_EACH_PTR(phi); + FINISH_PTR_LIST(par); + return err; +} + +static int check_user(struct instruction *insn, pseudo_t pseudo) +{ + struct instruction *def; + + if (!pseudo) { + show_entry(insn->bb->ep); + sparse_error(insn->pos, "null pseudo in %s", show_instruction(insn)); + return 1; + } + switch (pseudo->type) { + case PSEUDO_PHI: + case PSEUDO_REG: + def = pseudo->def; + if (def && def->bb) + break; + show_entry(insn->bb->ep); + sparse_error(insn->pos, "wrong usage for %s in %s", show_pseudo(pseudo), + show_instruction(insn)); + return 1; + + default: + break; + } + return 0; +} + +static int check_branch(struct entrypoint *ep, struct instruction *insn, struct basic_block *bb) +{ + if (bb->ep && lookup_bb(ep->bbs, bb)) + return 0; + sparse_error(insn->pos, "branch to dead BB: %s", show_instruction(insn)); + return 1; +} + +static int check_switch(struct entrypoint *ep, struct instruction *insn) +{ + struct multijmp *jmp; + int err = 0; + + FOR_EACH_PTR(insn->multijmp_list, jmp) { + err = check_branch(ep, insn, jmp->target); + if (err) + return err; + } END_FOR_EACH_PTR(jmp); + + return err; +} + +static int check_return(struct instruction *insn) +{ + struct symbol *ctype = insn->type; + + if (ctype && ctype->bit_size > 0 && insn->src == VOID) { + sparse_error(insn->pos, "return without value"); + return 1; + } + return 0; +} + +static int validate_insn(struct entrypoint *ep, struct instruction *insn) +{ + int err = 0; + + switch (insn->opcode) { + case OP_SEL: + case OP_RANGE: + err += check_user(insn, insn->src3); + /* fall through */ + + case OP_BINARY ... OP_BINCMP_END: + err += check_user(insn, insn->src2); + /* fall through */ + + case OP_UNOP ... OP_UNOP_END: + case OP_SLICE: + case OP_SYMADDR: + case OP_PHISOURCE: + err += check_user(insn, insn->src1); + break; + + case OP_CBR: + err += check_branch(ep, insn, insn->bb_true); + err += check_branch(ep, insn, insn->bb_false); + /* fall through */ + case OP_COMPUTEDGOTO: + err += check_user(insn, insn->cond); + break; + + case OP_PHI: + err += check_phi_node(insn); + break; + + case OP_CALL: + // FIXME: ignore for now + break; + + case OP_STORE: + err += check_user(insn, insn->target); + /* fall through */ + + case OP_LOAD: + err += check_user(insn, insn->src); + break; + + case OP_RET: + err += check_return(insn); + break; + + case OP_BR: + err += check_branch(ep, insn, insn->bb_true); + break; + case OP_SWITCH: + err += check_switch(ep, insn); + break; + + case OP_ENTRY: + case OP_SETVAL: + default: + break; + } + + return err; +} + +int ir_validate(struct entrypoint *ep) +{ + struct basic_block *bb; + int err = 0; + + if (!dbg_ir || has_error) + return 0; + + FOR_EACH_PTR(ep->bbs, bb) { + struct instruction *insn; + FOR_EACH_PTR(bb->insns, insn) { + if (!insn->bb) + continue; + err += validate_insn(ep, insn); + } END_FOR_EACH_PTR(insn); + } END_FOR_EACH_PTR(bb); + + if (err) + abort(); + return err; +} diff --git a/usr/src/tools/smatch/src/ir.h b/usr/src/tools/smatch/src/ir.h new file mode 100644 index 0000000000..48760c258c --- /dev/null +++ b/usr/src/tools/smatch/src/ir.h @@ -0,0 +1,8 @@ +#ifndef _IR_H +#define _IR_H + +#include "linearize.h" + +int ir_validate(struct entrypoint *ep); + +#endif diff --git a/usr/src/tools/smatch/src/lib.c b/usr/src/tools/smatch/src/lib.c index 9189d7885f..bc80001d55 100644 --- a/usr/src/tools/smatch/src/lib.c +++ b/usr/src/tools/smatch/src/lib.c @@ -23,6 +23,7 @@ * THE SOFTWARE. */ #include <ctype.h> +#include <errno.h> #include <fcntl.h> #include <stdarg.h> #include <stddef.h> @@ -40,20 +41,19 @@ #include "parse.h" #include "symbol.h" #include "expression.h" +#include "evaluate.h" #include "scope.h" #include "linearize.h" #include "target.h" +#include "machine.h" #include "version.h" +#include "bits.h" -static const char *progname; - -int sparse_errors = 0; -int sparse_warnings = 0; - -int verbose, optimize, optimize_size, preprocessing; +int verbose, optimize_level, optimize_size, preprocessing; int die_if_error = 0; int parse_error; int has_error = 0; +int do_output = 0; #ifndef __GNUC__ # define __GNUC__ 2 @@ -65,8 +65,12 @@ int gcc_major = __GNUC__; int gcc_minor = __GNUC_MINOR__; int gcc_patchlevel = __GNUC_PATCHLEVEL__; +const char *base_filename; + +static const char *diag_prefix = ""; static const char *gcc_base_dir = GCC_BASE; static const char *multiarch_dir = MULTIARCH_TRIPLET; +static const char *outfile = NULL; struct token *skip_to(struct token *token, int op) { @@ -75,10 +79,10 @@ struct token *skip_to(struct token *token, int op) return token; } +static struct token bad_token = { .pos.type = TOKEN_BAD }; struct token *expect(struct token *token, int op, const char *where) { if (!match_op(token, op)) { - static struct token bad_token; if (token != &bad_token) { bad_token.next = token; sparse_error(token->pos, "Expected %s %s", show_special(op), where); @@ -91,6 +95,21 @@ struct token *expect(struct token *token, int op, const char *where) return token->next; } +/// +// issue an error message on new parsing errors +// @token: the current token +// @errmsg: the error message +// If the current token is from a previous error, an error message +// has already been issued, so nothing more is done. +// Otherwise, @errmsg is displayed followed by the current token. +void unexpected(struct token *token, const char *errmsg) +{ + if (token == &bad_token) + return; + sparse_error(token->pos, "%s", errmsg); + sparse_error(token->pos, "got %s", show_token(token)); +} + unsigned int hexval(unsigned int c) { int retval = 256; @@ -113,14 +132,19 @@ static void do_warn(const char *type, struct position pos, const char * fmt, va_ static char buffer[512]; const char *name; + /* Shut up warnings if position is bad_token.pos */ + if (pos.type == TOKEN_BAD) + return; + vsprintf(buffer, fmt, args); name = stream_name(pos.stream); + fflush(stdout); fprintf(stderr, "%s: %s:%d:%d: %s%s\n", - progname, name, pos.line, pos.pos, type, buffer); + diag_prefix, name, pos.line, pos.pos, type, buffer); } -static int max_warnings = 100; +unsigned int fmax_warnings = 100; static int show_info = 1; void info(struct position pos, const char * fmt, ...) @@ -141,6 +165,9 @@ static void do_error(struct position pos, const char * fmt, va_list args) parse_error = 1; die_if_error = 1; show_info = 1; + /* Shut up warnings if position is bad_token.pos */ + if (pos.type == TOKEN_BAD) + return; /* Shut up warnings after an error */ has_error |= ERROR_CURR_PHASE; if (errors > 100) { @@ -167,12 +194,12 @@ void warning(struct position pos, const char * fmt, ...) return; } - if (!max_warnings || has_error) { + if (!fmax_warnings || has_error) { show_info = 0; return; } - if (!--max_warnings) { + if (!--fmax_warnings) { show_info = 0; fmt = "too many warnings"; } @@ -219,7 +246,7 @@ void die(const char *fmt, ...) vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); - fprintf(stderr, "%s: %s\n", progname, buffer); + fprintf(stderr, "%s: %s\n", diag_prefix, buffer); exit(1); } @@ -229,6 +256,8 @@ static struct token *pre_buffer_end = NULL; int Waddress = 0; int Waddress_space = 1; int Wbitwise = 1; +int Wbitwise_pointer = 0; +int Wcast_from_as = 0; int Wcast_to_as = 0; int Wcast_truncate = 1; int Wconstant_suffix = 0; @@ -241,6 +270,7 @@ int Wdesignated_init = 1; int Wdo_while = 0; int Wimplicit_int = 1; int Winit_cstring = 0; +int Wint_to_pointer_cast = 1; int Wenum_mismatch = 1; int Wexternal_function_has_definition = 1; int Wsparse_error = 0; @@ -254,9 +284,12 @@ int Woverride_init_all = 0; int Woverride_init_whole_range = 0; int Wparen_string = 0; int Wpointer_arith = 0; +int Wpointer_to_int_cast = 1; int Wptr_subtraction_blows = 0; int Wreturn_void = 0; int Wshadow = 0; +int Wshift_count_negative = 1; +int Wshift_count_overflow = 1; int Wsizeof_bool = 0; int Wstrict_prototypes = 1; int Wtautological_compare = 0; @@ -268,13 +301,20 @@ int Wunknown_attribute = 0; int Wvla = 1; int dump_macro_defs = 0; +int dump_macros_only = 0; -int dbg_entry = 0; +int dbg_compound = 0; int dbg_dead = 0; +int dbg_domtree = 0; +int dbg_entry = 0; +int dbg_ir = 0; +int dbg_postorder = 0; +unsigned long fdump_ir; int fmem_report = 0; -int fdump_linearize; unsigned long long fmemcpy_max_count = 100000; +unsigned long fpasses = ~0UL; +int funsigned_char = UNSIGNED_CHAR; int preprocess_only; @@ -286,25 +326,10 @@ static enum { STANDARD_C89, STANDARD_GNU89, STANDARD_GNU99, } standard = STANDARD_GNU89; -#define ARCH_LP32 0 -#define ARCH_LP64 1 -#define ARCH_LLP64 2 - -#ifdef __x86_64__ -#define ARCH_M64_DEFAULT ARCH_LP64 -#else -#define ARCH_M64_DEFAULT ARCH_LP32 -#endif - int arch_m64 = ARCH_M64_DEFAULT; int arch_msize_long = 0; - -#ifdef __BIG_ENDIAN__ -#define ARCH_BIG_ENDIAN 1 -#else -#define ARCH_BIG_ENDIAN 0 -#endif int arch_big_endian = ARCH_BIG_ENDIAN; +int arch_mach = MACH_NATIVE; #define CMDLINE_INCLUDE 20 @@ -433,8 +458,10 @@ static char **handle_switch_m(char *arg, char **next) { if (!strcmp(arg, "m64")) { arch_m64 = ARCH_LP64; - } else if (!strcmp(arg, "m32")) { + } else if (!strcmp(arg, "m32") || !strcmp(arg, "m16")) { arch_m64 = ARCH_LP32; + } else if (!strcmp(arg, "mx32")) { + arch_m64 = ARCH_X32; } else if (!strcmp(arg, "msize-llp64")) { arch_m64 = ARCH_LLP64; } else if (!strcmp(arg, "msize-long")) { @@ -449,44 +476,6 @@ static char **handle_switch_m(char *arg, char **next) return next; } -static void handle_arch_m64_finalize(void) -{ - switch (arch_m64) { - case ARCH_LP32: - /* default values */ -#if defined(__x86_64__) || defined (__i386) - add_pre_buffer("#weak_define __i386__ 1\n"); - add_pre_buffer("#weak_define __i386 1\n"); - add_pre_buffer("#weak_define i386 1\n"); -#endif - return; - case ARCH_LP64: - bits_in_long = 64; - max_int_alignment = 8; - size_t_ctype = &ulong_ctype; - ssize_t_ctype = &long_ctype; - add_pre_buffer("#weak_define __LP64__ 1\n"); - add_pre_buffer("#weak_define __LP64 1\n"); - add_pre_buffer("#weak_define _LP64 1\n"); - goto case_64bit_common; - case ARCH_LLP64: - bits_in_long = 32; - max_int_alignment = 4; - size_t_ctype = &ullong_ctype; - ssize_t_ctype = &llong_ctype; - add_pre_buffer("#weak_define __LLP64__ 1\n"); - goto case_64bit_common; - case_64bit_common: - bits_in_pointer = 64; - pointer_alignment = 8; -#if defined(__x86_64__) || defined (__i386) - add_pre_buffer("#weak_define __x86_64__ 1\n"); - add_pre_buffer("#weak_define __x86_64 1\n"); -#endif - break; - } -} - static void handle_arch_msize_long_finalize(void) { if (arch_msize_long) { @@ -497,13 +486,80 @@ static void handle_arch_msize_long_finalize(void) static void handle_arch_finalize(void) { - handle_arch_m64_finalize(); handle_arch_msize_long_finalize(); } +static const char *match_option(const char *arg, const char *prefix) +{ + unsigned int n = strlen(prefix); + if (strncmp(arg, prefix, n) == 0) + return arg + n; + return NULL; +} + + +struct mask_map { + const char *name; + unsigned long mask; +}; + +static int apply_mask(unsigned long *val, const char *str, unsigned len, const struct mask_map *map, int neg) +{ + const char *name; + + for (;(name = map->name); map++) { + if (!strncmp(name, str, len) && !name[len]) { + if (neg == 0) + *val |= map->mask; + else + *val &= ~map->mask; + return 0; + } + } + return 1; +} + +static int handle_suboption_mask(const char *arg, const char *opt, const struct mask_map *map, unsigned long *flag) +{ + if (*opt == '\0') { + apply_mask(flag, "", 0, map, 0); + return 1; + } + if (*opt++ != '=') + return 0; + while (1) { + unsigned int len = strcspn(opt, ",+"); + int neg = 0; + if (len == 0) + goto end; + if (!strncmp(opt, "no-", 3)) { + opt += 3; + len -= 3; + neg = 1; + } + if (apply_mask(flag, opt, len, map, neg)) + die("error: wrong option '%.*s' for \'%s\'", len, opt, arg); + +end: + opt += len; + if (*opt++ == '\0') + break; + } + return 1; +} + + +#define OPT_INVERSE 1 +struct flag { + const char *name; + int *flag; + int (*fun)(const char *arg, const char *opt, const struct flag *, int options); + unsigned long mask; +}; -static int handle_simple_switch(const char *arg, const char *name, int *flag) +static int handle_switches(const char *ori, const char *opt, const struct flag *flags) { + const char *arg = opt; int val = 1; // Prefixe "no-" mean to turn flag off. @@ -512,33 +568,79 @@ static int handle_simple_switch(const char *arg, const char *name, int *flag) val = 0; } - if (strcmp(arg, name) == 0) { - *flag = val; - return 1; + for (; flags->name; flags++) { + const char *opt = match_option(arg, flags->name); + int rc; + + if (!opt) + continue; + + if (flags->fun) { + int options = 0; + if (!val) + options |= OPT_INVERSE; + if ((rc = flags->fun(ori, opt, flags, options))) + return rc; + } + + // boolean flag + if (opt[0] == '\0' && flags->flag) { + if (flags->mask & OPT_INVERSE) + val = !val; + *flags->flag = val; + return 1; + } } // not handled return 0; } + +#define OPTNUM_ZERO_IS_INF 1 +#define OPTNUM_UNLIMITED 2 + +#define OPT_NUMERIC(NAME, TYPE, FUNCTION) \ +static int opt_##NAME(const char *arg, const char *opt, TYPE *ptr, int flag) \ +{ \ + char *end; \ + TYPE val; \ + \ + val = FUNCTION(opt, &end, 0); \ + if (*end != '\0' || end == opt) { \ + if ((flag & OPTNUM_UNLIMITED) && !strcmp(opt, "unlimited")) \ + val = ~val; \ + else \ + die("error: wrong argument to \'%s\'", arg); \ + } \ + if ((flag & OPTNUM_ZERO_IS_INF) && val == 0) \ + val = ~val; \ + *ptr = val; \ + return 1; \ +} + +OPT_NUMERIC(ullong, unsigned long long, strtoull) +OPT_NUMERIC(uint, unsigned int, strtoul) + + static char **handle_switch_o(char *arg, char **next) { if (!strcmp (arg, "o")) { // "-o foo" if (!*++next) die("argument to '-o' is missing"); + outfile = *next; } // else "-ofoo" return next; } -static const struct warning { - const char *name; - int *flag; -} warnings[] = { +static const struct flag warnings[] = { { "address", &Waddress }, { "address-space", &Waddress_space }, { "bitwise", &Wbitwise }, + { "bitwise-pointer", &Wbitwise_pointer}, + { "cast-from-as", &Wcast_from_as }, { "cast-to-as", &Wcast_to_as }, { "cast-truncate", &Wcast_truncate }, { "constant-suffix", &Wconstant_suffix }, @@ -553,6 +655,7 @@ static const struct warning { { "external-function-has-definition", &Wexternal_function_has_definition }, { "implicit-int", &Wimplicit_int }, { "init-cstring", &Winit_cstring }, + { "int-to-pointer-cast", &Wint_to_pointer_cast }, { "memcpy-max-count", &Wmemcpy_max_count }, { "non-pointer-null", &Wnon_pointer_null }, { "old-initializer", &Wold_initializer }, @@ -561,9 +664,12 @@ static const struct warning { { "override-init", &Woverride_init }, { "override-init-all", &Woverride_init_all }, { "paren-string", &Wparen_string }, + { "pointer-to-int-cast", &Wpointer_to_int_cast }, { "ptr-subtraction-blows", &Wptr_subtraction_blows }, { "return-void", &Wreturn_void }, { "shadow", &Wshadow }, + { "shift-count-negative", &Wshift_count_negative }, + { "shift-count-overflow", &Wshift_count_overflow }, { "sizeof-bool", &Wsizeof_bool }, { "strict-prototypes", &Wstrict_prototypes }, { "pointer-arith", &Wpointer_arith }, @@ -584,7 +690,7 @@ enum { }; -static char **handle_onoff_switch(char *arg, char **next, const struct warning warnings[], int n) +static char **handle_onoff_switch(char *arg, char **next, const struct flag warnings[], int n) { int flag = WARNING_ON; char *p = arg + 1; @@ -595,6 +701,7 @@ static char **handle_onoff_switch(char *arg, char **next, const struct warning w if (*warnings[i].flag != WARNING_FORCE_OFF && warnings[i].flag != &Wsparse_error) *warnings[i].flag = WARNING_ON; } + return NULL; } // Prefixes "no" and "no-" mean to turn warning off. @@ -626,9 +733,13 @@ static char **handle_switch_W(char *arg, char **next) return next; } -static struct warning debugs[] = { - { "entry", &dbg_entry}, +static struct flag debugs[] = { + { "compound", &dbg_compound}, { "dead", &dbg_dead}, + { "domtree", &dbg_domtree}, + { "entry", &dbg_entry}, + { "ir", &dbg_ir}, + { "postorder", &dbg_postorder}, }; @@ -645,21 +756,39 @@ static char **handle_switch_v(char *arg, char **next) return next; } -static struct warning dumps[] = { - { "D", &dump_macro_defs}, -}; - static char **handle_switch_d(char *arg, char **next) { - char ** ret = handle_onoff_switch(arg, next, dumps, ARRAY_SIZE(dumps)); - if (ret) - return ret; + char *arg_char = arg + 1; + /* + * -d<CHARS>, where <CHARS> is a sequence of characters, not preceded + * by a space. If you specify characters whose behaviour conflicts, + * the result is undefined. + */ + while (*arg_char) { + switch (*arg_char) { + case 'M': /* dump just the macro definitions */ + dump_macros_only = 1; + dump_macro_defs = 0; + break; + case 'D': /* like 'M', but also output pre-processed text */ + dump_macro_defs = 1; + dump_macros_only = 0; + break; + case 'N': /* like 'D', but only output macro names not bodies */ + break; + case 'I': /* like 'D', but also output #include directives */ + break; + case 'U': /* like 'D', but only output expanded macros */ + break; + } + arg_char++; + } return next; } -static void handle_onoff_switch_finalize(const struct warning warnings[], int n) +static void handle_onoff_switch_finalize(const struct flag warnings[], int n) { unsigned i; @@ -717,88 +846,115 @@ static char **handle_switch_O(char *arg, char **next) int level = 1; if (arg[1] >= '0' && arg[1] <= '9') level = arg[1] - '0'; - optimize = level; + optimize_level = level; optimize_size = arg[1] == 's'; return next; } -static char **handle_switch_fmemcpy_max_count(char *arg, char **next) +static int handle_ftabstop(const char *arg, const char *opt, const struct flag *flag, int options) { - unsigned long long val; - char *end; - - val = strtoull(arg, &end, 0); - if (*end != '\0' || end == arg) - die("error: missing argument to \"-fmemcpy-max-count=\""); - - if (val == 0) - val = ~0ULL; - fmemcpy_max_count = val; - return next; -} - -static char **handle_switch_ftabstop(char *arg, char **next) -{ - char *end; unsigned long val; + char *end; - if (*arg == '\0') - die("error: missing argument to \"-ftabstop=\""); + if (*opt == '\0') + die("error: missing argument to \"%s\"", arg); /* we silently ignore silly values */ - val = strtoul(arg, &end, 10); + val = strtoul(opt, &end, 10); if (*end == '\0' && 1 <= val && val <= 100) tabstop = val; - return next; + return 1; } -static int funsigned_char; -static void handle_funsigned_char(void) +static int handle_fpasses(const char *arg, const char *opt, const struct flag *flag, int options) { - if (funsigned_char) { - char_ctype.ctype.modifiers &= ~MOD_SIGNED; - char_ctype.ctype.modifiers |= MOD_UNSIGNED; + unsigned long mask; + + mask = flag->mask; + if (*opt == '\0') { + if (options & OPT_INVERSE) + fpasses &= ~mask; + else + fpasses |= mask; + return 1; + } + if (options & OPT_INVERSE) + return 0; + if (!strcmp(opt, "-enable")) { + fpasses |= mask; + return 1; + } + if (!strcmp(opt, "-disable")) { + fpasses &= ~mask; + return 1; + } + if (!strcmp(opt, "=last")) { + // clear everything above + mask |= mask - 1; + fpasses &= mask; + return 1; } + return 0; } - static char **handle_switch_fdump(char *arg, char **next) +static int handle_fdiagnostic_prefix(const char *arg, const char *opt, const struct flag *flag, int options) { - if (!strncmp(arg, "linearize", 9)) { - arg += 9; - if (*arg == '\0') - fdump_linearize = 1; - else if (!strcmp(arg, "=only")) - fdump_linearize = 2; - else - goto err; + switch (*opt) { + case '\0': + diag_prefix = "sparse"; + return 1; + case '=': + diag_prefix = xasprintf("%s", opt+1); + return 1; + default: + return 0; } +} - /* ignore others flags */ - return next; +static int handle_fdump_ir(const char *arg, const char *opt, const struct flag *flag, int options) +{ + static const struct mask_map dump_ir_options[] = { + { "", PASS_LINEARIZE }, + { "linearize", PASS_LINEARIZE }, + { "mem2reg", PASS_MEM2REG }, + { "final", PASS_FINAL }, + { }, + }; -err: - die("error: unknown flag \"-fdump-%s\"", arg); + return handle_suboption_mask(arg, opt, dump_ir_options, &fdump_ir); } -static char **handle_switch_f(char *arg, char **next) +static int handle_fmemcpy_max_count(const char *arg, const char *opt, const struct flag *flag, int options) { - arg++; + opt_ullong(arg, opt, &fmemcpy_max_count, OPTNUM_ZERO_IS_INF|OPTNUM_UNLIMITED); + return 1; +} - if (!strncmp(arg, "tabstop=", 8)) - return handle_switch_ftabstop(arg+8, next); - if (!strncmp(arg, "dump-", 5)) - return handle_switch_fdump(arg+5, next); - if (!strncmp(arg, "memcpy-max-count=", 17)) - return handle_switch_fmemcpy_max_count(arg+17, next); +static int handle_fmax_warnings(const char *arg, const char *opt, const struct flag *flag, int options) +{ + opt_uint(arg, opt, &fmax_warnings, OPTNUM_UNLIMITED); + return 1; +} - if (!strcmp(arg, "unsigned-char")) { - funsigned_char = 1; - return next; - } +static struct flag fflags[] = { + { "diagnostic-prefix", NULL, handle_fdiagnostic_prefix }, + { "dump-ir", NULL, handle_fdump_ir }, + { "linearize", NULL, handle_fpasses, PASS_LINEARIZE }, + { "max-warnings=", NULL, handle_fmax_warnings }, + { "mem-report", &fmem_report }, + { "memcpy-max-count=", NULL, handle_fmemcpy_max_count }, + { "tabstop=", NULL, handle_ftabstop }, + { "mem2reg", NULL, handle_fpasses, PASS_MEM2REG }, + { "optim", NULL, handle_fpasses, PASS_OPTIM }, + { "signed-char", &funsigned_char, NULL, OPT_INVERSE }, + { "unsigned-char", &funsigned_char, NULL, }, + { }, +}; - /* handle switches w/ arguments above, boolean and only boolean below */ - if (handle_simple_switch(arg, "mem-report", &fmem_report)) +static char **handle_switch_f(char *arg, char **next) +{ + if (handle_switches(arg-1, arg+1, fflags)) return next; return next; @@ -820,12 +976,9 @@ static char **handle_switch_a(char *arg, char **next) return next; } -static char **handle_switch_s(char *arg, char **next) +static char **handle_switch_s(const char *arg, char **next) { - if (!strncmp (arg, "std=", 4)) - { - arg += 4; - + if ((arg = match_option(arg, "std="))) { if (!strcmp (arg, "c89") || !strcmp (arg, "iso9899:1990")) standard = STANDARD_C89; @@ -896,6 +1049,13 @@ static char **handle_switch_g(char *arg, char **next) return next; } +static char **handle_switch_x(char *arg, char **next) +{ + if (!*++next) + die("missing argument for -x option"); + return next; +} + static char **handle_version(char *arg, char **next) { printf("%s\n", SPARSE_VERSION); @@ -910,7 +1070,6 @@ static char **handle_param(char *arg, char **next) if (strcmp(arg, "-mapper") == 0) return next; - /* For now just skip any '--param=*' or '--param *' */ if (*arg == '\0') { value = *++next; @@ -972,6 +1131,7 @@ static char **handle_switch(char *arg, char **next) case 'U': return handle_switch_U(arg, next); case 'v': return handle_switch_v(arg, next); case 'W': return handle_switch_W(arg, next); + case 'x': return handle_switch_x(arg, next); case '-': return handle_long_options(arg + 1, next); default: break; @@ -984,246 +1144,283 @@ static char **handle_switch(char *arg, char **next) return next; } -static void predefined_sizeof(const char *name, unsigned bits) +#define PTYPE_SIZEOF (1U << 0) +#define PTYPE_T (1U << 1) +#define PTYPE_MAX (1U << 2) +#define PTYPE_MIN (1U << 3) +#define PTYPE_WIDTH (1U << 4) +#define PTYPE_TYPE (1U << 5) +#define PTYPE_ALL (PTYPE_MAX|PTYPE_SIZEOF|PTYPE_WIDTH) +#define PTYPE_ALL_T (PTYPE_MAX|PTYPE_SIZEOF|PTYPE_WIDTH|PTYPE_T) + +static void predefined_sizeof(const char *name, const char *suffix, unsigned bits) +{ + char buf[32]; + + snprintf(buf, sizeof(buf), "__SIZEOF_%s%s__", name, suffix); + predefine(buf, 1, "%d", bits/8); +} + +static void predefined_width(const char *name, unsigned bits) { - add_pre_buffer("#weak_define __SIZEOF_%s__ %d\n", name, bits/8); + char buf[32]; + + snprintf(buf, sizeof(buf), "__%s_WIDTH__", name); + predefine(buf, 1, "%d", bits); } -static void predefined_max(const char *name, const char *suffix, unsigned bits) +static void predefined_max(const char *name, struct symbol *type) { - unsigned long long max = (1ULL << (bits - 1 )) - 1; + const char *suffix = builtin_type_suffix(type); + unsigned bits = type->bit_size - is_signed_type(type); + unsigned long long max = bits_mask(bits); + char buf[32]; + + snprintf(buf, sizeof(buf), "__%s_MAX__", name); + predefine(buf, 1, "%#llx%s", max, suffix); +} + +static void predefined_min(const char *name, struct symbol *type) +{ + const char *suffix = builtin_type_suffix(type); + char buf[32]; + + snprintf(buf, sizeof(buf), "__%s_MIN__", name); - add_pre_buffer("#weak_define __%s_MAX__ %#llx%s\n", name, max, suffix); + if (is_signed_type(type)) + predefine(buf, 1, "(-__%s_MAX__ - 1)", name); + else + predefine(buf, 1, "0%s", suffix); +} + +static void predefined_type(const char *name, struct symbol *type) +{ + const char *typename = builtin_typename(type); + add_pre_buffer("#weak_define __%s_TYPE__ %s\n", name, typename); } -static void predefined_type_size(const char *name, const char *suffix, unsigned bits) +static void predefined_ctype(const char *name, struct symbol *type, int flags) { - predefined_max(name, suffix, bits); - predefined_sizeof(name, bits); + unsigned bits = type->bit_size; + + if (flags & PTYPE_SIZEOF) { + const char *suffix = (flags & PTYPE_T) ? "_T" : ""; + predefined_sizeof(name, suffix, bits); + } + if (flags & PTYPE_MAX) + predefined_max(name, type); + if (flags & PTYPE_MIN) + predefined_min(name, type); + if (flags & PTYPE_TYPE) + predefined_type(name, type); + if (flags & PTYPE_WIDTH) + predefined_width(name, bits); } static void predefined_macros(void) { - add_pre_buffer("#define __CHECKER__ 1\n"); + predefine("__CHECKER__", 0, "1"); + predefine("__GNUC__", 1, "%d", gcc_major); + predefine("__GNUC_MINOR__", 1, "%d", gcc_minor); + predefine("__GNUC_PATCHLEVEL__", 1, "%d", gcc_patchlevel); - predefined_sizeof("SHORT", bits_in_short); - predefined_max("SHRT", "", bits_in_short); - predefined_max("SCHAR", "", bits_in_char); - predefined_max("WCHAR", "", bits_in_wchar); - add_pre_buffer("#weak_define __CHAR_BIT__ %d\n", bits_in_char); + predefine("__STDC__", 1, "1"); + switch (standard) { + case STANDARD_C89: + predefine("__STRICT_ANSI__", 1, "1"); + break; - predefined_type_size("INT", "", bits_in_int); - predefined_type_size("LONG", "L", bits_in_long); - predefined_type_size("LONG_LONG", "LL", bits_in_longlong); + case STANDARD_C94: + predefine("__STDC_VERSION__", 1, "199409L"); + predefine("__STRICT_ANSI__", 1, "1"); + break; - predefined_sizeof("INT128", 128); + case STANDARD_C99: + predefine("__STDC_VERSION__", 1, "199901L"); + predefine("__STRICT_ANSI__", 1, "1"); + break; - predefined_sizeof("SIZE_T", bits_in_pointer); - predefined_sizeof("PTRDIFF_T", bits_in_pointer); - predefined_sizeof("POINTER", bits_in_pointer); + case STANDARD_GNU89: + default: + break; - predefined_sizeof("FLOAT", bits_in_float); - predefined_sizeof("DOUBLE", bits_in_double); - predefined_sizeof("LONG_DOUBLE", bits_in_longdouble); + case STANDARD_GNU99: + predefine("__STDC_VERSION__", 1, "199901L"); + break; - add_pre_buffer("#weak_define __%s_ENDIAN__ 1\n", - arch_big_endian ? "BIG" : "LITTLE"); + case STANDARD_C11: + predefine("__STRICT_ANSI__", 1, "1"); + case STANDARD_GNU11: + predefine("__STDC_NO_ATOMICS__", 1, "1"); + predefine("__STDC_NO_COMPLEX__", 1, "1"); + predefine("__STDC_NO_THREADS__", 1, "1"); + predefine("__STDC_VERSION__", 1, "201112L"); + break; + } - add_pre_buffer("#weak_define __ORDER_LITTLE_ENDIAN__ 1234\n"); - add_pre_buffer("#weak_define __ORDER_BIG_ENDIAN__ 4321\n"); - add_pre_buffer("#weak_define __ORDER_PDP_ENDIAN__ 3412\n"); - add_pre_buffer("#weak_define __BYTE_ORDER__ __ORDER_%s_ENDIAN__\n", - arch_big_endian ? "BIG" : "LITTLE"); + predefine("__CHAR_BIT__", 1, "%d", bits_in_char); + if (funsigned_char) + predefine("__CHAR_UNSIGNED__", 1, "1"); + + predefined_ctype("SHORT", &short_ctype, PTYPE_SIZEOF); + predefined_ctype("SHRT", &short_ctype, PTYPE_MAX|PTYPE_WIDTH); + predefined_ctype("SCHAR", &schar_ctype, PTYPE_MAX|PTYPE_WIDTH); + predefined_ctype("WCHAR", wchar_ctype, PTYPE_ALL_T|PTYPE_MIN|PTYPE_TYPE); + predefined_ctype("WINT", wint_ctype, PTYPE_ALL_T|PTYPE_MIN|PTYPE_TYPE); + predefined_ctype("CHAR16", &ushort_ctype, PTYPE_TYPE); + predefined_ctype("CHAR32", &uint_ctype, PTYPE_TYPE); + + predefined_ctype("INT", &int_ctype, PTYPE_ALL); + predefined_ctype("LONG", &long_ctype, PTYPE_ALL); + predefined_ctype("LONG_LONG", &llong_ctype, PTYPE_ALL); + + predefined_ctype("INT8", &schar_ctype, PTYPE_MAX|PTYPE_TYPE); + predefined_ctype("UINT8", &uchar_ctype, PTYPE_MAX|PTYPE_TYPE); + predefined_ctype("INT16", &short_ctype, PTYPE_MAX|PTYPE_TYPE); + predefined_ctype("UINT16", &ushort_ctype, PTYPE_MAX|PTYPE_TYPE); + predefined_ctype("INT32", int32_ctype, PTYPE_MAX|PTYPE_TYPE); + predefined_ctype("UINT32", uint32_ctype, PTYPE_MAX|PTYPE_TYPE); + predefined_ctype("INT64", int64_ctype, PTYPE_MAX|PTYPE_TYPE); + predefined_ctype("UINT64", uint64_ctype, PTYPE_MAX|PTYPE_TYPE); + + predefined_sizeof("INT128", "", 128); + + predefined_ctype("INTMAX", intmax_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); + predefined_ctype("UINTMAX", uintmax_ctype, PTYPE_MAX|PTYPE_TYPE); + predefined_ctype("INTPTR", ssize_t_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); + predefined_ctype("UINTPTR", size_t_ctype, PTYPE_MAX|PTYPE_TYPE); + predefined_ctype("PTRDIFF", ssize_t_ctype, PTYPE_ALL_T|PTYPE_TYPE); + predefined_ctype("SIZE", size_t_ctype, PTYPE_ALL_T|PTYPE_TYPE); + predefined_ctype("POINTER", &ptr_ctype, PTYPE_SIZEOF); + + predefined_sizeof("FLOAT", "", bits_in_float); + predefined_sizeof("DOUBLE", "", bits_in_double); + predefined_sizeof("LONG_DOUBLE", "", bits_in_longdouble); + + predefine("__ORDER_LITTLE_ENDIAN__", 1, "1234"); + predefine("__ORDER_BIG_ENDIAN__", 1, "4321"); + predefine("__ORDER_PDP_ENDIAN__", 1, "3412"); + if (arch_big_endian) { + predefine("__BIG_ENDIAN__", 1, "1"); + predefine("__BYTE_ORDER__", 1, "__ORDER_BIG_ENDIAN__"); + } else { + predefine("__LITTLE_ENDIAN__", 1, "1"); + predefine("__BYTE_ORDER__", 1, "__ORDER_LITTLE_ENDIAN__"); + } - add_pre_buffer("#weak_define __PRAGMA_REDEFINE_EXTNAME 1\n"); + if (optimize_level) + predefine("__OPTIMIZE__", 0, "1"); + if (optimize_size) + predefine("__OPTIMIZE_SIZE__", 0, "1"); + + // Temporary hacks + predefine("__extension__", 0, NULL); + predefine("__pragma__", 0, NULL); + + switch (arch_m64) { + case ARCH_LP32: + break; + case ARCH_X32: + predefine("__ILP32__", 1, "1"); + predefine("_ILP32", 1, "1"); + break; + case ARCH_LP64: + predefine("__LP64__", 1, "1"); + predefine("__LP64", 1, "1"); + predefine("_LP64", 1, "1"); + break; + case ARCH_LLP64: + predefine("__LLP64__", 1, "1"); + break; + } + + switch (arch_mach) { + case MACH_ARM64: + predefine("__aarch64__", 1, "1"); + break; + case MACH_ARM: + predefine("__arm__", 1, "1"); + break; + case MACH_M68K: + predefine("__m68k__", 1, "1"); + break; + case MACH_MIPS64: + if (arch_m64 == ARCH_LP64) + predefine("__mips64", 1, "64"); + /* fall-through */ + case MACH_MIPS32: + predefine("__mips", 1, "%d", ptr_ctype.bit_size); + predefine("_MIPS_SZINT", 1, "%d", int_ctype.bit_size); + predefine("_MIPS_SZLONG", 1, "%d", long_ctype.bit_size); + predefine("_MIPS_SZPTR", 1, "%d", ptr_ctype.bit_size); + break; + case MACH_PPC64: + if (arch_m64 == ARCH_LP64) { + predefine("__powerpc64__", 1, "1"); + predefine("__ppc64__", 1, "1"); + predefine("__PPC64__", 1, "1"); + } + /* fall-through */ + case MACH_PPC32: + predefine("__powerpc__", 1, "1"); + predefine("__powerpc", 1, "1"); + predefine("__ppc__", 1, "1"); + predefine("__PPC__", 1, "1"); + break; + case MACH_RISCV64: + case MACH_RISCV32: + predefine("__riscv", 1, "1"); + predefine("__riscv_xlen", 1, "%d", ptr_ctype.bit_size); + break; + case MACH_S390X: + predefine("__zarch__", 1, "1"); + predefine("__s390x__", 1, "1"); + predefine("__s390__", 1, "1"); + break; + case MACH_SPARC64: + if (arch_m64 == ARCH_LP64) { + predefine("__sparc_v9__", 1, "1"); + predefine("__sparcv9__", 1, "1"); + predefine("__sparcv9", 1, "1"); + predefine("__sparc64__", 1, "1"); + predefine("__arch64__", 1, "1"); + } + /* fall-through */ + case MACH_SPARC32: + predefine("__sparc__", 1, "1"); + predefine("__sparc", 1, "1"); + break; + case MACH_X86_64: + if (arch_m64 != ARCH_LP32) { + predefine("__x86_64__", 1, "1"); + predefine("__x86_64", 1, "1"); + break; + } + /* fall-through */ + case MACH_I386: + predefine("__i386__", 1, "1"); + predefine("__i386", 1, "1"); + predefine("i386", 1, "1"); + break; + } + + predefine("__PRAGMA_REDEFINE_EXTNAME", 1, "1"); - /* - * This is far from perfect... - */ #ifdef __sun - add_pre_buffer("#weak_define __unix__ 1\n"); - add_pre_buffer("#weak_define __unix 1\n"); - add_pre_buffer("#weak_define unix 1\n"); - add_pre_buffer("#weak_define __sun__ 1\n"); - add_pre_buffer("#weak_define __sun 1\n"); - add_pre_buffer("#weak_define sun 1\n"); - add_pre_buffer("#weak_define __svr4__ 1\n"); + predefine("__unix__", 1, "1"); + predefine("__unix", 1, "1"); + predefine("unix", 1, "1"); + predefine("__sun__", 1, "1"); + predefine("__sun", 1, "1"); + predefine("sun", 1, "1"); + predefine("__svr4__", 1, "1"); #endif } -void declare_builtin_functions(void) -{ - /* Gaah. gcc knows tons of builtin <string.h> functions */ - add_pre_buffer("extern void *__builtin_memchr(const void *, int, __SIZE_TYPE__);\n"); - add_pre_buffer("extern void *__builtin_memcpy(void *, const void *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern void *__builtin_mempcpy(void *, const void *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern void *__builtin_memmove(void *, const void *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern void *__builtin_memset(void *, int, __SIZE_TYPE__);\n"); - add_pre_buffer("extern int __builtin_memcmp(const void *, const void *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern char *__builtin_strcat(char *, const char *);\n"); - add_pre_buffer("extern char *__builtin_strncat(char *, const char *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern int __builtin_strcmp(const char *, const char *);\n"); - add_pre_buffer("extern int __builtin_strncmp(const char *, const char *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern int __builtin_strcasecmp(const char *, const char *);\n"); - add_pre_buffer("extern int __builtin_strncasecmp(const char *, const char *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern char *__builtin_strchr(const char *, int);\n"); - add_pre_buffer("extern char *__builtin_strrchr(const char *, int);\n"); - add_pre_buffer("extern char *__builtin_strcpy(char *, const char *);\n"); - add_pre_buffer("extern char *__builtin_strncpy(char *, const char *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern char *__builtin_strdup(const char *);\n"); - add_pre_buffer("extern char *__builtin_strndup(const char *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern __SIZE_TYPE__ __builtin_strspn(const char *, const char *);\n"); - add_pre_buffer("extern __SIZE_TYPE__ __builtin_strcspn(const char *, const char *);\n"); - add_pre_buffer("extern char * __builtin_strpbrk(const char *, const char *);\n"); - add_pre_buffer("extern char* __builtin_stpcpy(const char *, const char*);\n"); - add_pre_buffer("extern char* __builtin_stpncpy(const char *, const char*, __SIZE_TYPE__);\n"); - add_pre_buffer("extern __SIZE_TYPE__ __builtin_strlen(const char *);\n"); - add_pre_buffer("extern char *__builtin_strstr(const char *, const char *);\n"); - add_pre_buffer("extern char *__builtin_strcasestr(const char *, const char *);\n"); - add_pre_buffer("extern char *__builtin_strnstr(const char *, const char *, __SIZE_TYPE__);\n"); - - /* And even some from <strings.h> */ - add_pre_buffer("extern int __builtin_bcmp(const void *, const void *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern void __builtin_bcopy(const void *, void *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern void __builtin_bzero(void *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern char*__builtin_index(const char *, int);\n"); - add_pre_buffer("extern char*__builtin_rindex(const char *, int);\n"); - - /* And bitwise operations.. */ - add_pre_buffer("extern int __builtin_clrsb(int);\n"); - add_pre_buffer("extern int __builtin_clrsbl(long);\n"); - add_pre_buffer("extern int __builtin_clrsbll(long long);\n"); - add_pre_buffer("extern int __builtin_clz(int);\n"); - add_pre_buffer("extern int __builtin_clzl(long);\n"); - add_pre_buffer("extern int __builtin_clzll(long long);\n"); - add_pre_buffer("extern int __builtin_ctz(int);\n"); - add_pre_buffer("extern int __builtin_ctzl(long);\n"); - add_pre_buffer("extern int __builtin_ctzll(long long);\n"); - add_pre_buffer("extern int __builtin_ffs(int);\n"); - add_pre_buffer("extern int __builtin_ffsl(long);\n"); - add_pre_buffer("extern int __builtin_ffsll(long long);\n"); - add_pre_buffer("extern int __builtin_parity(unsigned int);\n"); - add_pre_buffer("extern int __builtin_parityl(unsigned long);\n"); - add_pre_buffer("extern int __builtin_parityll(unsigned long long);\n"); - add_pre_buffer("extern int __builtin_popcount(unsigned int);\n"); - add_pre_buffer("extern int __builtin_popcountl(unsigned long);\n"); - add_pre_buffer("extern int __builtin_popcountll(unsigned long long);\n"); - - /* And byte swaps.. */ - add_pre_buffer("extern unsigned short __builtin_bswap16(unsigned short);\n"); - add_pre_buffer("extern unsigned int __builtin_bswap32(unsigned int);\n"); - add_pre_buffer("extern unsigned long long __builtin_bswap64(unsigned long long);\n"); - - /* And atomic memory access functions.. */ - add_pre_buffer("extern int __sync_fetch_and_add(void *, ...);\n"); - add_pre_buffer("extern int __sync_fetch_and_sub(void *, ...);\n"); - add_pre_buffer("extern int __sync_fetch_and_or(void *, ...);\n"); - add_pre_buffer("extern int __sync_fetch_and_and(void *, ...);\n"); - add_pre_buffer("extern int __sync_fetch_and_xor(void *, ...);\n"); - add_pre_buffer("extern int __sync_fetch_and_nand(void *, ...);\n"); - add_pre_buffer("extern int __sync_add_and_fetch(void *, ...);\n"); - add_pre_buffer("extern int __sync_sub_and_fetch(void *, ...);\n"); - add_pre_buffer("extern int __sync_or_and_fetch(void *, ...);\n"); - add_pre_buffer("extern int __sync_and_and_fetch(void *, ...);\n"); - add_pre_buffer("extern int __sync_xor_and_fetch(void *, ...);\n"); - add_pre_buffer("extern int __sync_nand_and_fetch(void *, ...);\n"); - add_pre_buffer("extern int __sync_bool_compare_and_swap(void *, ...);\n"); - add_pre_buffer("extern int __sync_val_compare_and_swap(void *, ...);\n"); - add_pre_buffer("extern void __sync_synchronize();\n"); - add_pre_buffer("extern int __sync_lock_test_and_set(void *, ...);\n"); - add_pre_buffer("extern void __sync_lock_release(void *, ...);\n"); - - /* And some random ones.. */ - add_pre_buffer("extern void *__builtin_return_address(unsigned int);\n"); - add_pre_buffer("extern void *__builtin_extract_return_addr(void *);\n"); - add_pre_buffer("extern void *__builtin_frame_address(unsigned int);\n"); - add_pre_buffer("extern void __builtin_trap(void);\n"); - add_pre_buffer("extern void *__builtin_alloca(__SIZE_TYPE__);\n"); - add_pre_buffer("extern void __builtin_prefetch (const void *, ...);\n"); - add_pre_buffer("extern long __builtin_alpha_extbl(long, long);\n"); - add_pre_buffer("extern long __builtin_alpha_extwl(long, long);\n"); - add_pre_buffer("extern long __builtin_alpha_insbl(long, long);\n"); - add_pre_buffer("extern long __builtin_alpha_inswl(long, long);\n"); - add_pre_buffer("extern long __builtin_alpha_insql(long, long);\n"); - add_pre_buffer("extern long __builtin_alpha_inslh(long, long);\n"); - add_pre_buffer("extern long __builtin_alpha_cmpbge(long, long);\n"); - add_pre_buffer("extern int __builtin_abs(int);\n"); - add_pre_buffer("extern long __builtin_labs(long);\n"); - add_pre_buffer("extern long long __builtin_llabs(long long);\n"); - add_pre_buffer("extern double __builtin_fabs(double);\n"); - add_pre_buffer("extern __SIZE_TYPE__ __builtin_va_arg_pack_len(void);\n"); - - /* Add Blackfin-specific stuff */ - add_pre_buffer( - "#ifdef __bfin__\n" - "extern void __builtin_bfin_csync(void);\n" - "extern void __builtin_bfin_ssync(void);\n" - "extern int __builtin_bfin_norm_fr1x32(int);\n" - "#endif\n" - ); - - /* And some floating point stuff.. */ - add_pre_buffer("extern int __builtin_isgreater(float, float);\n"); - add_pre_buffer("extern int __builtin_isgreaterequal(float, float);\n"); - add_pre_buffer("extern int __builtin_isless(float, float);\n"); - add_pre_buffer("extern int __builtin_islessequal(float, float);\n"); - add_pre_buffer("extern int __builtin_islessgreater(float, float);\n"); - add_pre_buffer("extern int __builtin_isunordered(float, float);\n"); - - /* And some INFINITY / NAN stuff.. */ - add_pre_buffer("extern double __builtin_huge_val(void);\n"); - add_pre_buffer("extern float __builtin_huge_valf(void);\n"); - add_pre_buffer("extern long double __builtin_huge_vall(void);\n"); - add_pre_buffer("extern double __builtin_inf(void);\n"); - add_pre_buffer("extern float __builtin_inff(void);\n"); - add_pre_buffer("extern long double __builtin_infl(void);\n"); - add_pre_buffer("extern double __builtin_nan(const char *);\n"); - add_pre_buffer("extern float __builtin_nanf(const char *);\n"); - add_pre_buffer("extern long double __builtin_nanl(const char *);\n"); - add_pre_buffer("extern int __builtin_isinf_sign(float);\n"); - add_pre_buffer("extern int __builtin_isfinite(float);\n"); - add_pre_buffer("extern int __builtin_isnan(float);\n"); - - /* And some __FORTIFY_SOURCE ones.. */ - add_pre_buffer ("extern __SIZE_TYPE__ __builtin_object_size(const void *, int);\n"); - add_pre_buffer ("extern void * __builtin___memcpy_chk(void *, const void *, __SIZE_TYPE__, __SIZE_TYPE__);\n"); - add_pre_buffer ("extern void * __builtin___memmove_chk(void *, const void *, __SIZE_TYPE__, __SIZE_TYPE__);\n"); - add_pre_buffer ("extern void * __builtin___mempcpy_chk(void *, const void *, __SIZE_TYPE__, __SIZE_TYPE__);\n"); - add_pre_buffer ("extern void * __builtin___memset_chk(void *, int, __SIZE_TYPE__, __SIZE_TYPE__);\n"); - add_pre_buffer ("extern int __builtin___sprintf_chk(char *, int, __SIZE_TYPE__, const char *, ...);\n"); - add_pre_buffer ("extern int __builtin___snprintf_chk(char *, __SIZE_TYPE__, int , __SIZE_TYPE__, const char *, ...);\n"); - add_pre_buffer ("extern char * __builtin___stpcpy_chk(char *, const char *, __SIZE_TYPE__);\n"); - add_pre_buffer ("extern char * __builtin___strcat_chk(char *, const char *, __SIZE_TYPE__);\n"); - add_pre_buffer ("extern char * __builtin___strcpy_chk(char *, const char *, __SIZE_TYPE__);\n"); - add_pre_buffer ("extern char * __builtin___strncat_chk(char *, const char *, __SIZE_TYPE__, __SIZE_TYPE__);\n"); - add_pre_buffer ("extern char * __builtin___strncpy_chk(char *, const char *, __SIZE_TYPE__, __SIZE_TYPE__);\n"); - add_pre_buffer ("extern int __builtin___vsprintf_chk(char *, int, __SIZE_TYPE__, const char *, __builtin_va_list);\n"); - add_pre_buffer ("extern int __builtin___vsnprintf_chk(char *, __SIZE_TYPE__, int, __SIZE_TYPE__, const char *, __builtin_va_list ap);\n"); - add_pre_buffer ("extern void __builtin_unreachable(void);\n"); - - /* And some from <stdlib.h> */ - add_pre_buffer("extern void __builtin_abort(void);\n"); - add_pre_buffer("extern void *__builtin_calloc(__SIZE_TYPE__, __SIZE_TYPE__);\n"); - add_pre_buffer("extern void __builtin_exit(int);\n"); - add_pre_buffer("extern void *__builtin_malloc(__SIZE_TYPE__);\n"); - add_pre_buffer("extern void *__builtin_realloc(void *, __SIZE_TYPE__);\n"); - add_pre_buffer("extern void __builtin_free(void *);\n"); - - /* And some from <stdio.h> */ - add_pre_buffer("extern int __builtin_printf(const char *, ...);\n"); - add_pre_buffer("extern int __builtin_sprintf(char *, const char *, ...);\n"); - add_pre_buffer("extern int __builtin_snprintf(char *, __SIZE_TYPE__, const char *, ...);\n"); - add_pre_buffer("extern int __builtin_puts(const char *);\n"); - add_pre_buffer("extern int __builtin_vprintf(const char *, __builtin_va_list);\n"); - add_pre_buffer("extern int __builtin_vsprintf(char *, const char *, __builtin_va_list);\n"); - add_pre_buffer("extern int __builtin_vsnprintf(char *, __SIZE_TYPE__, const char *, __builtin_va_list ap);\n"); -} - -void create_builtin_stream(void) -{ - add_pre_buffer("#weak_define __GNUC__ %d\n", gcc_major); - add_pre_buffer("#weak_define __GNUC_MINOR__ %d\n", gcc_minor); - add_pre_buffer("#weak_define __GNUC_PATCHLEVEL__ %d\n", gcc_patchlevel); +static void create_builtin_stream(void) +{ + // Temporary hack + add_pre_buffer("#define _Pragma(x)\n"); /* add the multiarch include directories, if any */ if (multiarch_dir && *multiarch_dir) { @@ -1236,57 +1433,8 @@ void create_builtin_stream(void) add_pre_buffer("#add_system \"%s/include\"\n", gcc_base_dir); add_pre_buffer("#add_system \"%s/include-fixed\"\n", gcc_base_dir); - add_pre_buffer("#define __extension__\n"); - add_pre_buffer("#define __pragma__\n"); - add_pre_buffer("#define _Pragma(x)\n"); - - // gcc defines __SIZE_TYPE__ to be size_t. For linux/i86 and - // solaris/sparc that is really "unsigned int" and for linux/x86_64 - // it is "long unsigned int". In either case we can probably - // get away with this. We need the #weak_define as cgcc will define - // the right __SIZE_TYPE__. - if (size_t_ctype == &ulong_ctype) - add_pre_buffer("#weak_define __SIZE_TYPE__ long unsigned int\n"); - else - add_pre_buffer("#weak_define __SIZE_TYPE__ unsigned int\n"); - add_pre_buffer("#weak_define __STDC__ 1\n"); - - switch (standard) - { - case STANDARD_C89: - add_pre_buffer("#weak_define __STRICT_ANSI__\n"); - break; - - case STANDARD_C94: - add_pre_buffer("#weak_define __STDC_VERSION__ 199409L\n"); - add_pre_buffer("#weak_define __STRICT_ANSI__\n"); - break; - - case STANDARD_C99: - add_pre_buffer("#weak_define __STDC_VERSION__ 199901L\n"); - add_pre_buffer("#weak_define __STRICT_ANSI__\n"); - break; - - case STANDARD_GNU89: - break; - - case STANDARD_GNU99: - add_pre_buffer("#weak_define __STDC_VERSION__ 199901L\n"); - break; - - case STANDARD_C11: - add_pre_buffer("#weak_define __STRICT_ANSI__ 1\n"); - case STANDARD_GNU11: - add_pre_buffer("#weak_define __STDC_NO_ATOMICS__ 1\n"); - add_pre_buffer("#weak_define __STDC_NO_COMPLEX__ 1\n"); - add_pre_buffer("#weak_define __STDC_NO_THREADS__ 1\n"); - add_pre_buffer("#weak_define __STDC_VERSION__ 201112L\n"); - break; - - default: - assert (0); - } - + add_pre_buffer("#define __has_builtin(x) 0\n"); + add_pre_buffer("#define __has_attribute(x) 0\n"); add_pre_buffer("#define __builtin_stdarg_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n"); add_pre_buffer("#define __builtin_va_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n"); add_pre_buffer("#define __builtin_ms_va_start(a,b) ((a) = (__builtin_ms_va_list)(&(b)))\n"); @@ -1298,14 +1446,6 @@ void create_builtin_stream(void) add_pre_buffer("#define __builtin_va_end(arg)\n"); add_pre_buffer("#define __builtin_ms_va_end(arg)\n"); add_pre_buffer("#define __builtin_va_arg_pack()\n"); - - /* FIXME! We need to do these as special magic macros at expansion time! */ - add_pre_buffer("#define __BASE_FILE__ \"base_file.c\"\n"); - - if (optimize) - add_pre_buffer("#define __OPTIMIZE__ 1\n"); - if (optimize_size) - add_pre_buffer("#define __OPTIMIZE_SIZE__ 1\n"); } static struct symbol_list *sparse_tokenstream(struct token *token) @@ -1315,8 +1455,12 @@ static struct symbol_list *sparse_tokenstream(struct token *token) // Preprocess the stream token = preprocess(token); - if (dump_macro_defs && !builtin) - dump_macro_definitions(); + if (dump_macro_defs || dump_macros_only) { + if (!builtin) + dump_macro_definitions(); + if (dump_macros_only) + return NULL; + } if (preprocess_only) { while (!eof_token(token)) { @@ -1357,6 +1501,7 @@ static struct symbol_list *sparse_file(const char *filename) if (fd < 0) die("No such file: %s", filename); } + base_filename = filename; // Tokenize the input stream token = tokenize(filename, fd, NULL, includepath); @@ -1366,12 +1511,6 @@ static struct symbol_list *sparse_file(const char *filename) return sparse_tokenstream(token); } -static int endswith(const char *str, const char *suffix) -{ - const char *found = strstr(str, suffix); - return (found && strcmp(found, suffix) == 0); -} - /* * This handles the "-include" directive etc: we're in global * scope, and all types/macros etc will affect all the following @@ -1393,6 +1532,12 @@ static struct symbol_list *sparse_initial(void) return sparse_tokenstream(pre_buffer_begin); } +static int endswith(const char *str, const char *suffix) +{ + const char *found = strstr(str, suffix); + return (found && strcmp(found, suffix) == 0); +} + struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list **filelist) { char **args; @@ -1402,7 +1547,7 @@ struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list init_symbols(); init_include_path(); - progname = argv[0]; + diag_prefix = argv[0]; args = argv; for (;;) { @@ -1419,23 +1564,32 @@ struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list endswith(arg, ".so.1") || endswith(arg, ".o")) continue; - add_ptr_list_notag(filelist, arg); + add_ptr_list(filelist, arg); } handle_switch_W_finalize(); handle_switch_v_finalize(); - handle_arch_finalize(); + // Redirect stdout if needed + if (dump_macro_defs || preprocess_only) + do_output = 1; + if (do_output && outfile && strcmp(outfile, "-")) { + if (!freopen(outfile, "w", stdout)) + die("error: cannot open %s: %s", outfile, strerror(errno)); + } + + if (fdump_ir == 0) + fdump_ir = PASS_FINAL; list = NULL; - if (!ptr_list_empty(filelist)) { + if (filelist) { // Initialize type system + init_target(); + handle_arch_finalize(); init_ctype(); - handle_funsigned_char(); - create_builtin_stream(); predefined_macros(); - if (!preprocess_only) - declare_builtin_functions(); + create_builtin_stream(); + declare_builtins(); list = sparse_initial(); diff --git a/usr/src/tools/smatch/src/lib.h b/usr/src/tools/smatch/src/lib.h index 9d9a6ee187..de8f1132fa 100644 --- a/usr/src/tools/smatch/src/lib.h +++ b/usr/src/tools/smatch/src/lib.h @@ -1,6 +1,7 @@ #ifndef LIB_H #define LIB_H +#include <stdbool.h> #include <stdlib.h> #include <stddef.h> @@ -32,6 +33,8 @@ #include "compat.h" #include "ptrlist.h" +#include "utils.h" +#include "bits.h" #define DO_STRINGIFY(x) #x #define STRINGIFY(x) DO_STRINGIFY(x) @@ -40,12 +43,15 @@ #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) #endif -extern int verbose, optimize, optimize_size, preprocessing; +extern int verbose, optimize_level, optimize_size, preprocessing; extern int die_if_error; extern int parse_error; -extern int repeat_phase, merge_phi_sources; +extern int repeat_phase; +extern int do_output; extern int gcc_major, gcc_minor, gcc_patchlevel; +extern const char *base_filename; + extern unsigned int hexval(unsigned int c); struct position { @@ -83,6 +89,8 @@ typedef struct pseudo *pseudo_t; struct token *skip_to(struct token *, int); struct token *expect(struct token *, int, const char *); +void unexpected(struct token *, const char *errmsg); + #ifdef __GNUC__ #define FORMAT_ATTR(pos) __attribute__ ((__format__ (__printf__, pos, pos+1))) #define NORETURN_ATTR __attribute__ ((__noreturn__)) @@ -108,13 +116,32 @@ extern void expression_error(struct expression *, const char *, ...) FORMAT_ATTR #define ERROR_PREV_PHASE (1 << 1) extern int has_error; + +enum phase { + PASS__PARSE, + PASS__LINEARIZE, + PASS__MEM2REG, + PASS__OPTIM, + PASS__FINAL, +}; + +#define PASS_PARSE (1UL << PASS__PARSE) +#define PASS_LINEARIZE (1UL << PASS__LINEARIZE) +#define PASS_MEM2REG (1UL << PASS__MEM2REG) +#define PASS_OPTIM (1UL << PASS__OPTIM) +#define PASS_FINAL (1UL << PASS__FINAL) + + extern void add_pre_buffer(const char *fmt, ...) FORMAT_ATTR(1); +extern void predefine(const char *name, int weak, const char *fmt, ...) FORMAT_ATTR(3); extern int preprocess_only; extern int Waddress; extern int Waddress_space; extern int Wbitwise; +extern int Wbitwise_pointer; +extern int Wcast_from_as; extern int Wcast_to_as; extern int Wcast_truncate; extern int Wconstant_suffix; @@ -130,6 +157,7 @@ extern int Wexternal_function_has_definition; extern int Wsparse_error; extern int Wimplicit_int; extern int Winit_cstring; +extern int Wint_to_pointer_cast; extern int Wmemcpy_max_count; extern int Wnon_pointer_null; extern int Wold_initializer; @@ -140,9 +168,12 @@ extern int Woverride_init_all; extern int Woverride_init_whole_range; extern int Wparen_string; extern int Wpointer_arith; +extern int Wpointer_to_int_cast; extern int Wptr_subtraction_blows; extern int Wreturn_void; extern int Wshadow; +extern int Wshift_count_negative; +extern int Wshift_count_overflow; extern int Wsizeof_bool; extern int Wstrict_prototypes; extern int Wtautological_compare; @@ -154,20 +185,27 @@ extern int Wunknown_attribute; extern int Wvla; extern int dump_macro_defs; +extern int dump_macros_only; -extern int dbg_entry; +extern int dbg_compound; extern int dbg_dead; +extern int dbg_domtree; +extern int dbg_entry; +extern int dbg_ir; +extern int dbg_postorder; +extern unsigned int fmax_warnings; extern int fmem_report; -extern int fdump_linearize; +extern unsigned long fdump_ir; extern unsigned long long fmemcpy_max_count; +extern unsigned long fpasses; +extern int funsigned_char; extern int arch_m64; extern int arch_msize_long; extern int arch_big_endian; +extern int arch_mach; -extern void declare_builtin_functions(void); -extern void create_builtin_stream(void); extern void dump_macro_definitions(void); extern struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list **files); extern struct symbol_list *__sparse(char *filename); @@ -207,7 +245,7 @@ static inline int bb_list_size(struct basic_block_list *list) static inline void free_instruction_list(struct instruction_list **head) { - free_ptr_list((struct ptr_list **)head); + free_ptr_list(head); } static inline struct instruction * delete_last_instruction(struct instruction_list **head) @@ -215,11 +253,6 @@ static inline struct instruction * delete_last_instruction(struct instruction_li return undo_ptr_list_last((struct ptr_list **)head); } -static inline struct basic_block * delete_last_basic_block(struct basic_block_list **head) -{ - return delete_ptr_list_last((struct ptr_list **)head); -} - static inline struct basic_block *first_basic_block(struct basic_block_list *head) { return first_ptr_list((struct ptr_list *)head); diff --git a/usr/src/tools/smatch/src/linearize.c b/usr/src/tools/smatch/src/linearize.c index 2aa3acb2c1..415bf7e50c 100644 --- a/usr/src/tools/smatch/src/linearize.c +++ b/usr/src/tools/smatch/src/linearize.c @@ -19,12 +19,14 @@ #include "parse.h" #include "expression.h" #include "linearize.h" +#include "optimize.h" #include "flow.h" #include "target.h" -pseudo_t linearize_statement(struct entrypoint *ep, struct statement *stmt); -pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr); +static pseudo_t linearize_statement(struct entrypoint *ep, struct statement *stmt); +static pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr); +static pseudo_t add_cast(struct entrypoint *ep, struct symbol *to, struct symbol *from, int op, pseudo_t src); static pseudo_t add_binary_op(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t left, pseudo_t right); static pseudo_t add_setval(struct entrypoint *ep, struct symbol *ctype, struct expression *val); static pseudo_t linearize_one_symbol(struct entrypoint *ep, struct symbol *sym); @@ -70,14 +72,13 @@ static struct basic_block *alloc_basic_block(struct entrypoint *ep, struct posit { static int nr; struct basic_block *bb = __alloc_basic_block(0); - bb->context = -1; bb->pos = pos; bb->ep = ep; bb->nr = nr++; return bb; } -static struct multijmp *alloc_multijmp(struct basic_block *target, int begin, int end) +static struct multijmp *alloc_multijmp(struct basic_block *target, long long begin, long long end) { struct multijmp *multijmp = __alloc_multijmp(0); multijmp->target = target; @@ -86,12 +87,16 @@ static struct multijmp *alloc_multijmp(struct basic_block *target, int begin, in return multijmp; } -static inline int regno(pseudo_t n) +const char *show_label(struct basic_block *bb) { - int retval = -1; - if (n && n->type == PSEUDO_REG) - retval = n->nr; - return retval; + static int n; + static char buffer[4][16]; + char *buf = buffer[3 & ++n]; + + if (!bb) + return ".L???"; + snprintf(buf, 64, ".L%u", bb->nr); + return buf; } const char *show_pseudo(pseudo_t pseudo) @@ -111,8 +116,12 @@ const char *show_pseudo(pseudo_t pseudo) struct symbol *sym = pseudo->sym; struct expression *expr; + if (!sym) { + snprintf(buf, 64, "<bad symbol>"); + break; + } if (sym->bb_target) { - snprintf(buf, 64, ".L%u", sym->bb_target->nr); + snprintf(buf, 64, "%s", show_label(sym->bb_target)); break; } if (sym->ident) { @@ -120,7 +129,7 @@ const char *show_pseudo(pseudo_t pseudo) break; } expr = sym->initializer; - snprintf(buf, 64, "<anon symbol:%p>", sym); + snprintf(buf, 64, "<anon symbol:%p>", verbose ? sym : NULL); if (expr) { switch (expr->type) { case EXPR_VALUE: @@ -155,6 +164,8 @@ const char *show_pseudo(pseudo_t pseudo) if (pseudo->ident) sprintf(buf+i, "(%s)", show_ident(pseudo->ident)); break; + case PSEUDO_UNDEF: + return "UNDEF"; default: snprintf(buf, 64, "<bad pseudo type %d>", pseudo->type); } @@ -172,15 +183,12 @@ static const char *opcodes[] = { [OP_BR] = "br", [OP_CBR] = "cbr", [OP_SWITCH] = "switch", - [OP_INVOKE] = "invoke", [OP_COMPUTEDGOTO] = "jmp *", - [OP_UNWIND] = "unwind", /* Binary */ [OP_ADD] = "add", [OP_SUB] = "sub", - [OP_MULU] = "mulu", - [OP_MULS] = "muls", + [OP_MUL] = "mul", [OP_DIVU] = "divu", [OP_DIVS] = "divs", [OP_MODU] = "modu", @@ -189,12 +197,16 @@ static const char *opcodes[] = { [OP_LSR] = "lsr", [OP_ASR] = "asr", + /* Floating-point Binary */ + [OP_FADD] = "fadd", + [OP_FSUB] = "fsub", + [OP_FMUL] = "fmul", + [OP_FDIV] = "fdiv", + /* Logical */ [OP_AND] = "and", [OP_OR] = "or", [OP_XOR] = "xor", - [OP_AND_BOOL] = "and-bool", - [OP_OR_BOOL] = "or-bool", /* Binary comparison */ [OP_SET_EQ] = "seteq", @@ -208,37 +220,54 @@ static const char *opcodes[] = { [OP_SET_BE] = "setbe", [OP_SET_AE] = "setae", + /* floating-point comparison */ + [OP_FCMP_ORD] = "fcmpord", + [OP_FCMP_OEQ] = "fcmpoeq", + [OP_FCMP_ONE] = "fcmpone", + [OP_FCMP_OLE] = "fcmpole", + [OP_FCMP_OGE] = "fcmpoge", + [OP_FCMP_OLT] = "fcmpolt", + [OP_FCMP_OGT] = "fcmpogt", + [OP_FCMP_UEQ] = "fcmpueq", + [OP_FCMP_UNE] = "fcmpune", + [OP_FCMP_ULE] = "fcmpule", + [OP_FCMP_UGE] = "fcmpuge", + [OP_FCMP_ULT] = "fcmpult", + [OP_FCMP_UGT] = "fcmpugt", + [OP_FCMP_UNO] = "fcmpuno", + /* Uni */ [OP_NOT] = "not", [OP_NEG] = "neg", + [OP_FNEG] = "fneg", /* Special three-input */ [OP_SEL] = "select", /* Memory */ - [OP_MALLOC] = "malloc", - [OP_FREE] = "free", - [OP_ALLOCA] = "alloca", [OP_LOAD] = "load", [OP_STORE] = "store", [OP_SETVAL] = "set", + [OP_SETFVAL] = "setfval", [OP_SYMADDR] = "symaddr", - [OP_GET_ELEMENT_PTR] = "getelem", /* Other */ [OP_PHI] = "phi", [OP_PHISOURCE] = "phisrc", - [OP_CAST] = "cast", - [OP_SCAST] = "scast", - [OP_FPCAST] = "fpcast", + [OP_SEXT] = "sext", + [OP_ZEXT] = "zext", + [OP_TRUNC] = "trunc", + [OP_FCVTU] = "fcvtu", + [OP_FCVTS] = "fcvts", + [OP_UCVTF] = "ucvtf", + [OP_SCVTF] = "scvtf", + [OP_FCVTF] = "fcvtf", + [OP_UTPTR] = "utptr", + [OP_PTRTU] = "ptrtu", [OP_PTRCAST] = "ptrcast", [OP_INLINED_CALL] = "# call", [OP_CALL] = "call", - [OP_VANEXT] = "va_next", - [OP_VAARG] = "va_arg", [OP_SLICE] = "slice", - [OP_SNOP] = "snop", - [OP_LNOP] = "lnop", [OP_NOP] = "nop", [OP_DEATHNOTE] = "dead", [OP_ASM] = "asm", @@ -307,34 +336,15 @@ const char *show_instruction(struct instruction *insn) break; case OP_CBR: - buf += sprintf(buf, "%s, .L%u, .L%u", show_pseudo(insn->cond), insn->bb_true->nr, insn->bb_false->nr); + buf += sprintf(buf, "%s, %s, %s", show_pseudo(insn->cond), show_label(insn->bb_true), show_label(insn->bb_false)); break; case OP_BR: - buf += sprintf(buf, ".L%u", insn->bb_true->nr); + buf += sprintf(buf, "%s", show_label(insn->bb_true)); break; - case OP_SYMADDR: { - struct symbol *sym = insn->symbol->sym; - buf += sprintf(buf, "%s <- ", show_pseudo(insn->target)); - - if (!insn->bb && !sym) - break; - if (sym->bb_target) { - buf += sprintf(buf, ".L%u", sym->bb_target->nr); - break; - } - if (sym->ident) { - buf += sprintf(buf, "%s", show_ident(sym->ident)); - break; - } - buf += sprintf(buf, "<anon symbol:%p>", sym); - break; - } - case OP_SETVAL: { struct expression *expr = insn->val; - struct symbol *sym; buf += sprintf(buf, "%s <- ", show_pseudo(insn->target)); if (!expr) { @@ -347,7 +357,7 @@ const char *show_instruction(struct instruction *insn) buf += sprintf(buf, "%lld", expr->value); break; case EXPR_FVALUE: - buf += sprintf(buf, "%Lf", expr->fvalue); + buf += sprintf(buf, "%Le", expr->fvalue); break; case EXPR_STRING: buf += sprintf(buf, "%.40s", show_string(expr->string)); @@ -356,33 +366,36 @@ const char *show_instruction(struct instruction *insn) buf += sprintf(buf, "%s", show_ident(expr->symbol->ident)); break; case EXPR_LABEL: - sym = expr->symbol; - if (sym->bb_target) - buf += sprintf(buf, ".L%u", sym->bb_target->nr); + buf += sprintf(buf, "%s", show_label(expr->symbol->bb_target)); break; default: buf += sprintf(buf, "SETVAL EXPR TYPE %d", expr->type); } break; } + case OP_SETFVAL: + buf += sprintf(buf, "%s <- ", show_pseudo(insn->target)); + buf += sprintf(buf, "%Le", insn->fvalue); + break; + case OP_SWITCH: { struct multijmp *jmp; buf += sprintf(buf, "%s", show_pseudo(insn->cond)); FOR_EACH_PTR(insn->multijmp_list, jmp) { if (jmp->begin == jmp->end) - buf += sprintf(buf, ", %d -> .L%u", jmp->begin, jmp->target->nr); + buf += sprintf(buf, ", %lld -> %s", jmp->begin, show_label(jmp->target)); else if (jmp->begin < jmp->end) - buf += sprintf(buf, ", %d ... %d -> .L%u", jmp->begin, jmp->end, jmp->target->nr); + buf += sprintf(buf, ", %lld ... %lld -> %s", jmp->begin, jmp->end, show_label(jmp->target)); else - buf += sprintf(buf, ", default -> .L%u", jmp->target->nr); + buf += sprintf(buf, ", default -> %s", show_label(jmp->target)); } END_FOR_EACH_PTR(jmp); break; } case OP_COMPUTEDGOTO: { struct multijmp *jmp; - buf += sprintf(buf, "%s", show_pseudo(insn->target)); + buf += sprintf(buf, "%s", show_pseudo(insn->src)); FOR_EACH_PTR(insn->multijmp_list, jmp) { - buf += sprintf(buf, ", .L%u", jmp->target->nr); + buf += sprintf(buf, ", %s", show_label(jmp->target)); } END_FOR_EACH_PTR(jmp); break; } @@ -401,15 +414,17 @@ const char *show_instruction(struct instruction *insn) const char *s = " <-"; buf += sprintf(buf, "%s", show_pseudo(insn->target)); FOR_EACH_PTR(insn->phi_list, phi) { + if (phi == VOID && !verbose) + continue; buf += sprintf(buf, "%s %s", s, show_pseudo(phi)); s = ","; } END_FOR_EACH_PTR(phi); break; } - case OP_LOAD: case OP_LNOP: + case OP_LOAD: buf += sprintf(buf, "%s <- %d[%s]", show_pseudo(insn->target), insn->offset, show_pseudo(insn->src)); break; - case OP_STORE: case OP_SNOP: + case OP_STORE: buf += sprintf(buf, "%s -> %d[%s]", show_pseudo(insn->target), insn->offset, show_pseudo(insn->src)); break; case OP_INLINED_CALL: @@ -423,9 +438,13 @@ const char *show_instruction(struct instruction *insn) } END_FOR_EACH_PTR(arg); break; } - case OP_CAST: - case OP_SCAST: - case OP_FPCAST: + case OP_SEXT: case OP_ZEXT: + case OP_TRUNC: + case OP_FCVTU: case OP_FCVTS: + case OP_UCVTF: case OP_SCVTF: + case OP_FCVTF: + case OP_UTPTR: + case OP_PTRTU: case OP_PTRCAST: buf += sprintf(buf, "%s <- (%d) %s", show_pseudo(insn->target), @@ -433,6 +452,7 @@ const char *show_instruction(struct instruction *insn) show_pseudo(insn->src)); break; case OP_BINARY ... OP_BINARY_END: + case OP_FPCMP ... OP_FPCMP_END: case OP_BINCMP ... OP_BINCMP_END: buf += sprintf(buf, "%s <- %s, %s", show_pseudo(insn->target), show_pseudo(insn->src1), show_pseudo(insn->src2)); break; @@ -447,6 +467,8 @@ const char *show_instruction(struct instruction *insn) break; case OP_NOT: case OP_NEG: + case OP_FNEG: + case OP_SYMADDR: buf += sprintf(buf, "%s <- %s", show_pseudo(insn->target), show_pseudo(insn->src1)); break; @@ -483,7 +505,7 @@ void show_bb(struct basic_block *bb) { struct instruction *insn; - printf(".L%u:\n", bb->nr); + printf("%s:\n", show_label(bb)); if (verbose) { pseudo_t needs, defines; printf("%s:%d\n", stream_name(bb->pos.stream), bb->pos.line); @@ -491,7 +513,7 @@ void show_bb(struct basic_block *bb) FOR_EACH_PTR(bb->needs, needs) { struct instruction *def = needs->def; if (def->opcode != OP_PHI) { - printf(" **uses %s (from .L%u)**\n", show_pseudo(needs), def->bb->nr); + printf(" **uses %s (from %s)**\n", show_pseudo(needs), show_label(def->bb)); } else { pseudo_t phi; const char *sep = " "; @@ -499,7 +521,7 @@ void show_bb(struct basic_block *bb) FOR_EACH_PTR(def->phi_list, phi) { if (phi == VOID) continue; - printf("%s(%s:.L%u)", sep, show_pseudo(phi), phi->def->bb->nr); + printf("%s(%s:%s)", sep, show_pseudo(phi), show_label(phi->def->bb)); sep = ", "; } END_FOR_EACH_PTR(phi); printf(")**\n"); @@ -513,7 +535,7 @@ void show_bb(struct basic_block *bb) if (bb->parents) { struct basic_block *from; FOR_EACH_PTR(bb->parents, from) { - printf(" **from .L%u (%s:%d:%d)**\n", from->nr, + printf(" **from %s (%s:%d:%d)**\n", show_label(from), stream_name(from->pos.stream), from->pos.line, from->pos.pos); } END_FOR_EACH_PTR(from); } @@ -521,7 +543,7 @@ void show_bb(struct basic_block *bb) if (bb->children) { struct basic_block *to; FOR_EACH_PTR(bb->children, to) { - printf(" **to .L%u (%s:%d:%d)**\n", to->nr, + printf(" **to %s (%s:%d:%d)**\n", show_label(to), stream_name(to->pos.stream), to->pos.line, to->pos.pos); } END_FOR_EACH_PTR(to); } @@ -685,7 +707,7 @@ void insert_select(struct basic_block *bb, struct instruction *br, struct instru /* Remove the 'br' */ delete_last_instruction(&bb->insns); - select = alloc_instruction(OP_SEL, phi_node->size); + select = alloc_typed_instruction(OP_SEL, phi_node->type); select->bb = bb; assert(br->cond); @@ -726,7 +748,7 @@ static struct basic_block * add_label(struct entrypoint *ep, struct symbol *labe return bb; } -static void add_branch(struct entrypoint *ep, struct expression *expr, pseudo_t cond, struct basic_block *bb_true, struct basic_block *bb_false) +static void add_branch(struct entrypoint *ep, pseudo_t cond, struct basic_block *bb_true, struct basic_block *bb_false) { struct basic_block *bb = ep->active; struct instruction *br; @@ -744,7 +766,6 @@ static void add_branch(struct entrypoint *ep, struct expression *expr, pseudo_t } } -/* Dummy pseudo allocator */ pseudo_t alloc_pseudo(struct instruction *def) { static int nr = 0; @@ -755,15 +776,6 @@ pseudo_t alloc_pseudo(struct instruction *def) return pseudo; } -static void clear_symbol_pseudos(struct entrypoint *ep) -{ - pseudo_t pseudo; - - FOR_EACH_PTR(ep->accesses, pseudo) { - pseudo->sym->pseudo = NULL; - } END_FOR_EACH_PTR(pseudo); -} - static pseudo_t symbol_pseudo(struct entrypoint *ep, struct symbol *sym) { pseudo_t pseudo; @@ -781,35 +793,39 @@ static pseudo_t symbol_pseudo(struct entrypoint *ep, struct symbol *sym) sym->pseudo = pseudo; add_pseudo(&ep->accesses, pseudo); } - /* Symbol pseudos have neither nr, usage nor def */ + /* Symbol pseudos have neither nr nor def */ return pseudo; } -pseudo_t value_pseudo(struct symbol *type, long long val) +pseudo_t value_pseudo(long long val) { #define MAX_VAL_HASH 64 static struct pseudo_list *prev[MAX_VAL_HASH]; int hash = val & (MAX_VAL_HASH-1); struct pseudo_list **list = prev + hash; - int size = type ? type->bit_size : value_size(val); pseudo_t pseudo; - FOR_EACH_PTR(*list, pseudo) { - if (pseudo->value == val && pseudo->size == size) + if (pseudo->value == val) return pseudo; } END_FOR_EACH_PTR(pseudo); pseudo = __alloc_pseudo(0); pseudo->type = PSEUDO_VAL; pseudo->value = val; - pseudo->size = size; add_pseudo(list, pseudo); /* Value pseudos have neither nr, usage nor def */ return pseudo; } +pseudo_t undef_pseudo(void) +{ + pseudo_t pseudo = __alloc_pseudo(0); + pseudo->type = PSEUDO_UNDEF; + return pseudo; +} + static pseudo_t argument_pseudo(struct entrypoint *ep, int nr) { pseudo_t pseudo = __alloc_pseudo(0); @@ -824,26 +840,68 @@ static pseudo_t argument_pseudo(struct entrypoint *ep, int nr) return pseudo; } -pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, int size) +struct instruction *alloc_phisrc(pseudo_t pseudo, struct symbol *type) { - struct instruction *insn; - pseudo_t phi; + struct instruction *insn = alloc_typed_instruction(OP_PHISOURCE, type); + pseudo_t phi = __alloc_pseudo(0); static int nr = 0; - if (!source) - return VOID; - - insn = alloc_instruction(OP_PHISOURCE, size); - phi = __alloc_pseudo(0); phi->type = PSEUDO_PHI; phi->nr = ++nr; phi->def = insn; use_pseudo(insn, pseudo, &insn->phi_src); - insn->bb = source; insn->target = phi; + return insn; +} + +pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, struct symbol *type) +{ + struct instruction *insn; + + if (!source) + return VOID; + + insn = alloc_phisrc(pseudo, type); + insn->bb = source; add_instruction(&source->insns, insn); - return phi; + return insn->target; +} + +struct instruction *alloc_phi_node(struct basic_block *bb, struct symbol *type, struct ident *ident) +{ + struct instruction *phi_node = alloc_typed_instruction(OP_PHI, type); + pseudo_t phi; + + phi = alloc_pseudo(phi_node); + phi->ident = ident; + phi->def = phi_node; + phi_node->target = phi; + phi_node->bb = bb; + return phi_node; +} + +void add_phi_node(struct basic_block *bb, struct instruction *phi_node) +{ + struct instruction *insn; + + FOR_EACH_PTR(bb->insns, insn) { + enum opcode op = insn->opcode; + if (op == OP_PHI) + continue; + INSERT_CURRENT(phi_node, insn); + return; + } END_FOR_EACH_PTR(insn); + + // FIXME + add_instruction(&bb->insns, phi_node); +} + +struct instruction *insert_phi_node(struct basic_block *bb, struct symbol *var) +{ + struct instruction *phi_node = alloc_phi_node(bb, var, var->ident); + add_phi_node(bb, phi_node); + return phi_node; } /* @@ -852,17 +910,12 @@ pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, int size) * information in one place. */ struct access_data { - struct symbol *result_type; // result ctype - struct symbol *source_type; // source ctype + struct symbol *type; // ctype + struct symbol *btype; // base type of bitfields pseudo_t address; // pseudo containing address .. unsigned int offset; // byte offset - struct position pos; }; -static void finish_address_gen(struct entrypoint *ep, struct access_data *ad) -{ -} - static int linearize_simple_address(struct entrypoint *ep, struct expression *addr, struct access_data *ad) @@ -884,7 +937,7 @@ static int linearize_simple_address(struct entrypoint *ep, return 1; } -static struct symbol *base_type(struct symbol *sym) +static struct symbol *bitfield_base_type(struct symbol *sym) { struct symbol *base = sym; @@ -905,9 +958,7 @@ static int linearize_address_gen(struct entrypoint *ep, if (!ctype) return 0; - ad->pos = expr->pos; - ad->result_type = ctype; - ad->source_type = base_type(ctype); + ad->type = ctype; if (expr->type == EXPR_PREOP && expr->op == '*') return linearize_simple_address(ep, expr->unop, ad); @@ -920,11 +971,15 @@ static pseudo_t add_load(struct entrypoint *ep, struct access_data *ad) struct instruction *insn; pseudo_t new; - insn = alloc_typed_instruction(OP_LOAD, ad->source_type); + if (!ep->active) + return VOID; + + insn = alloc_typed_instruction(OP_LOAD, ad->btype); new = alloc_pseudo(insn); insn->target = new; insn->offset = ad->offset; + insn->is_volatile = ad->type && (ad->type->ctype.modifiers & MOD_VOLATILE); use_pseudo(insn, ad->address, &insn->src); add_one_insn(ep, insn); return new; @@ -933,40 +988,75 @@ static pseudo_t add_load(struct entrypoint *ep, struct access_data *ad) static void add_store(struct entrypoint *ep, struct access_data *ad, pseudo_t value) { struct basic_block *bb = ep->active; + struct instruction *store; - if (bb_reachable(bb)) { - struct instruction *store = alloc_typed_instruction(OP_STORE, ad->source_type); - store->offset = ad->offset; - use_pseudo(store, value, &store->target); - use_pseudo(store, ad->address, &store->src); - add_one_insn(ep, store); + if (!bb) + return; + + store = alloc_typed_instruction(OP_STORE, ad->btype); + store->offset = ad->offset; + store->is_volatile = ad->type && (ad->type->ctype.modifiers & MOD_VOLATILE); + use_pseudo(store, value, &store->target); + use_pseudo(store, ad->address, &store->src); + add_one_insn(ep, store); +} + +static pseudo_t linearize_bitfield_insert(struct entrypoint *ep, + pseudo_t ori, pseudo_t val, struct symbol *ctype, struct symbol *btype) +{ + unsigned int shift = ctype->bit_offset; + unsigned int size = ctype->bit_size; + unsigned long long mask = ((1ULL << size) - 1); + unsigned long long smask= bits_mask(btype->bit_size); + + val = add_cast(ep, btype, ctype, OP_ZEXT, val); + if (shift) { + val = add_binary_op(ep, btype, OP_SHL, val, value_pseudo(shift)); + mask <<= shift; } + ori = add_binary_op(ep, btype, OP_AND, ori, value_pseudo(~mask & smask)); + val = add_binary_op(ep, btype, OP_OR, ori, val); + + return val; } static pseudo_t linearize_store_gen(struct entrypoint *ep, pseudo_t value, struct access_data *ad) { + struct symbol *ctype = ad->type; + struct symbol *btype; pseudo_t store = value; - if (type_size(ad->source_type) != type_size(ad->result_type)) { - struct symbol *ctype = ad->result_type; - unsigned int shift = ctype->bit_offset; - unsigned int size = ctype->bit_size; - pseudo_t orig = add_load(ep, ad); - unsigned long long mask = (1ULL << size) - 1; + if (!ep->active) + return VOID; - if (shift) { - store = add_binary_op(ep, ad->source_type, OP_SHL, value, value_pseudo(ctype, shift)); - mask <<= shift; - } - orig = add_binary_op(ep, ad->source_type, OP_AND, orig, value_pseudo(ctype, ~mask)); - store = add_binary_op(ep, ad->source_type, OP_OR, orig, store); + btype = ad->btype = bitfield_base_type(ctype); + if (type_size(btype) != type_size(ctype)) { + pseudo_t orig = add_load(ep, ad); + store = linearize_bitfield_insert(ep, orig, value, ctype, btype); } add_store(ep, ad, store); return value; } +static void taint_undefined_behaviour(struct instruction *insn) +{ + pseudo_t src2; + + switch (insn->opcode) { + case OP_LSR: + case OP_ASR: + case OP_SHL: + src2 = insn->src2; + if (src2->type != PSEUDO_VAL) + break; + if ((unsigned long long)src2->value >= insn->size) + insn->tainted = 1; + break; + } +} + static pseudo_t add_binary_op(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t left, pseudo_t right) { struct instruction *insn = alloc_typed_instruction(op, ctype); @@ -988,29 +1078,53 @@ static pseudo_t add_setval(struct entrypoint *ep, struct symbol *ctype, struct e return target; } +static pseudo_t add_setfval(struct entrypoint *ep, struct symbol *ctype, long double fval) +{ + struct instruction *insn = alloc_typed_instruction(OP_SETFVAL, ctype); + pseudo_t target = alloc_pseudo(insn); + insn->target = target; + insn->fvalue = fval; + add_one_insn(ep, insn); + return target; +} + static pseudo_t add_symbol_address(struct entrypoint *ep, struct symbol *sym) { struct instruction *insn = alloc_instruction(OP_SYMADDR, bits_in_pointer); pseudo_t target = alloc_pseudo(insn); insn->target = target; - use_pseudo(insn, symbol_pseudo(ep, sym), &insn->symbol); + use_pseudo(insn, symbol_pseudo(ep, sym), &insn->src); add_one_insn(ep, insn); return target; } -static pseudo_t linearize_load_gen(struct entrypoint *ep, struct access_data *ad) +static pseudo_t linearize_bitfield_extract(struct entrypoint *ep, + pseudo_t val, struct symbol *ctype, struct symbol *btype) { - struct symbol *ctype = ad->result_type; - pseudo_t new = add_load(ep, ad); + unsigned int off = ctype->bit_offset; - if (ctype->bit_offset) { - pseudo_t shift = value_pseudo(ctype, ctype->bit_offset); - pseudo_t newval = add_binary_op(ep, ad->source_type, OP_LSR, new, shift); - new = newval; + if (off) { + pseudo_t shift = value_pseudo(off); + val = add_binary_op(ep, btype, OP_LSR, val, shift); } - if (ctype->bit_size != type_size(ad->source_type)) - new = cast_pseudo(ep, new, ad->source_type, ad->result_type); + val = cast_pseudo(ep, val, btype, ctype); + return val; +} + +static pseudo_t linearize_load_gen(struct entrypoint *ep, struct access_data *ad) +{ + struct symbol *ctype = ad->type; + struct symbol *btype; + pseudo_t new; + + if (!ep->active) + return VOID; + + btype = ad->btype = bitfield_base_type(ctype); + new = add_load(ep, ad); + if (ctype->bit_size != type_size(btype)) + new = linearize_bitfield_extract(ep, new, ctype, btype); return new; } @@ -1022,31 +1136,36 @@ static pseudo_t linearize_access(struct entrypoint *ep, struct expression *expr) if (!linearize_address_gen(ep, expr, &ad)) return VOID; value = linearize_load_gen(ep, &ad); - finish_address_gen(ep, &ad); return value; } -/* FIXME: FP */ static pseudo_t linearize_inc_dec(struct entrypoint *ep, struct expression *expr, int postop) { struct access_data ad = { NULL, }; - pseudo_t old, new, one; + pseudo_t old, new, one; int op = expr->op == SPECIAL_INCREMENT ? OP_ADD : OP_SUB; if (!linearize_address_gen(ep, expr->unop, &ad)) return VOID; old = linearize_load_gen(ep, &ad); - one = value_pseudo(expr->ctype, expr->op_value); - new = add_binary_op(ep, expr->ctype, op, old, one); + op = opcode_float(op, expr->ctype); + if (is_float_type(expr->ctype)) + one = add_setfval(ep, expr->ctype, expr->op_value); + else + one = value_pseudo(expr->op_value); + if (ad.btype != ad.type) + old = cast_pseudo(ep, old, ad.type, ad.btype); + new = add_binary_op(ep, ad.btype, op, old, one); + if (ad.btype != ad.type) + new = cast_pseudo(ep, new, ad.btype, ad.type); linearize_store_gen(ep, new, &ad); - finish_address_gen(ep, &ad); return postop ? old : new; } -static pseudo_t add_uniop(struct entrypoint *ep, struct expression *expr, int op, pseudo_t src) +static pseudo_t add_unop(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t src) { - struct instruction *insn = alloc_typed_instruction(op, expr->ctype); + struct instruction *insn = alloc_typed_instruction(op, ctype); pseudo_t new = alloc_pseudo(insn); insn->target = new; @@ -1055,6 +1174,14 @@ static pseudo_t add_uniop(struct entrypoint *ep, struct expression *expr, int op return new; } +static pseudo_t add_cast(struct entrypoint *ep, struct symbol *to, + struct symbol *from, int op, pseudo_t src) +{ + pseudo_t new = add_unop(ep, to, op, src); + new->def->orig_type = from; + return new; +} + static pseudo_t linearize_slice(struct entrypoint *ep, struct expression *expr) { pseudo_t pre = linearize_expression(ep, expr->base); @@ -1072,17 +1199,18 @@ static pseudo_t linearize_slice(struct entrypoint *ep, struct expression *expr) static pseudo_t linearize_regular_preop(struct entrypoint *ep, struct expression *expr) { pseudo_t pre = linearize_expression(ep, expr->unop); + struct symbol *ctype = expr->ctype; switch (expr->op) { case '+': return pre; case '!': { - pseudo_t zero = value_pseudo(expr->ctype, 0); - return add_binary_op(ep, expr->ctype, OP_SET_EQ, pre, zero); + pseudo_t zero = value_pseudo(0); + return add_binary_op(ep, ctype, OP_SET_EQ, pre, zero); } case '~': - return add_uniop(ep, expr, OP_NOT, pre); + return add_unop(ep, ctype, OP_NOT, pre); case '-': - return add_uniop(ep, expr, OP_NEG, pre); + return add_unop(ep, ctype, opcode_float(OP_NEG, ctype), pre); } return VOID; } @@ -1112,28 +1240,125 @@ static pseudo_t linearize_postop(struct entrypoint *ep, struct expression *expr) * case, since you can't access through it anyway without another * cast. */ -static struct instruction *alloc_cast_instruction(struct symbol *src, struct symbol *ctype) +enum mtype { + MTYPE_UINT, + MTYPE_SINT, + MTYPE_PTR, + MTYPE_VPTR, // TODO: must be removed ? + MTYPE_FLOAT, + MTYPE_BAD, +}; + +static enum mtype get_mtype(struct symbol *s) +{ + int sign = (s->ctype.modifiers & MOD_SIGNED) ? 1 : 0; + +retry: switch (s->type) { + case SYM_NODE: + s = s->ctype.base_type; + goto retry; + case SYM_PTR: + if (s->ctype.base_type == &void_ctype) + return MTYPE_VPTR; + return MTYPE_PTR; + case SYM_BITFIELD: + case SYM_RESTRICT: + case SYM_FOULED: + case SYM_ENUM: + s = s->ctype.base_type; + /* fall-through */ + case_int: + return sign ? MTYPE_SINT : MTYPE_UINT; + case SYM_BASETYPE: + if (s->ctype.base_type == &fp_type) + return MTYPE_FLOAT; + if (s->ctype.base_type == &int_type) + goto case_int; + /* fall-through */ + default: + return MTYPE_BAD; + } +} + +static int get_cast_opcode(struct symbol *dst, struct symbol *src) { - int opcode = OP_CAST; - struct symbol *base = ctype; + enum mtype stype = get_mtype(src); + enum mtype dtype = get_mtype(dst); - if (src->ctype.modifiers & MOD_SIGNED) - opcode = OP_SCAST; - if (base->type == SYM_NODE) - base = base->ctype.base_type; - if (base->type == SYM_PTR) { - base = base->ctype.base_type; - if (base != &void_ctype) - opcode = OP_PTRCAST; - } else if (base->ctype.base_type == &fp_type) - opcode = OP_FPCAST; - return alloc_typed_instruction(opcode, ctype); + switch (dtype) { + case MTYPE_FLOAT: + switch (stype) { + case MTYPE_FLOAT: + if (dst->bit_size == src->bit_size) + return OP_NOP; + return OP_FCVTF; + case MTYPE_UINT: + return OP_UCVTF; + case MTYPE_SINT: + return OP_SCVTF; + default: + return OP_BADOP; + } + case MTYPE_PTR: + switch (stype) { + case MTYPE_UINT: + case MTYPE_SINT: + return OP_UTPTR; + case MTYPE_PTR: + case MTYPE_VPTR: + return OP_PTRCAST; + default: + return OP_BADOP; + } + case MTYPE_VPTR: + switch (stype) { + case MTYPE_PTR: + case MTYPE_VPTR: + case MTYPE_UINT: + stype = MTYPE_UINT; + /* fall through */ + case MTYPE_SINT: + break; + default: + return OP_BADOP; + } + /* fall through */ + case MTYPE_UINT: + case MTYPE_SINT: + switch (stype) { + case MTYPE_FLOAT: + return dtype == MTYPE_UINT ? OP_FCVTU : OP_FCVTS; + case MTYPE_PTR: + return OP_PTRTU; + case MTYPE_VPTR: + case MTYPE_UINT: + case MTYPE_SINT: + if (dst->bit_size ==src->bit_size) + return OP_NOP; + if (dst->bit_size < src->bit_size) + return OP_TRUNC; + return stype == MTYPE_SINT ? OP_SEXT : OP_ZEXT; + default: + return OP_BADOP; + } + /* fall through */ + default: + if (src->type == SYM_NODE) + src = src->ctype.base_type; + if (dst->type == SYM_NODE) + dst = dst->ctype.base_type; + if (src == dst) + return OP_NOP; + return OP_BADOP; + } } static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol *from, struct symbol *to) { + const struct position pos = current_pos; pseudo_t result; struct instruction *insn; + int opcode; if (src == VOID) return VOID; @@ -1141,7 +1366,33 @@ static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol * return VOID; if (from->bit_size < 0 || to->bit_size < 0) return VOID; - insn = alloc_cast_instruction(from, to); + opcode = get_cast_opcode(to, from); + switch (opcode) { + case OP_NOP: + return src; + case OP_UTPTR: + if (from->bit_size == to->bit_size) + break; + if (src == value_pseudo(0)) + break; + if (Wint_to_pointer_cast) + warning(pos, "non size-preserving integer to pointer cast"); + src = cast_pseudo(ep, src, from, size_t_ctype); + from = size_t_ctype; + break; + case OP_PTRTU: + if (from->bit_size == to->bit_size) + break; + if (Wpointer_to_int_cast) + warning(pos, "non size-preserving pointer to integer cast"); + src = cast_pseudo(ep, src, from, size_t_ctype); + return cast_pseudo(ep, src, size_t_ctype, to); + case OP_BADOP: + return VOID; + default: + break; + } + insn = alloc_typed_instruction(opcode, to); result = alloc_pseudo(insn); insn->target = result; insn->orig_type = from; @@ -1150,11 +1401,13 @@ static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol * return result; } -static int opcode_sign(int opcode, struct symbol *ctype) +static int map_opcode(int opcode, struct symbol *ctype) { + if (ctype && is_float_type(ctype)) + return opcode_table[opcode].to_float; if (ctype && (ctype->ctype.modifiers & MOD_SIGNED)) { switch(opcode) { - case OP_MULU: case OP_DIVU: case OP_MODU: case OP_LSR: + case OP_DIVU: case OP_MODU: case OP_LSR: opcode++; } } @@ -1166,10 +1419,19 @@ static inline pseudo_t add_convert_to_bool(struct entrypoint *ep, pseudo_t src, pseudo_t zero; int op; + if (!type || src == VOID) + return VOID; if (is_bool_type(type)) return src; - zero = value_pseudo(type, 0); - op = OP_SET_NE; + if (src->type == PSEUDO_VAL && (src->value == 0 || src->value == 1)) + return src; + if (is_float_type(type)) { + zero = add_setfval(ep, type, 0.0); + op = map_opcode(OP_SET_NE, type); + } else { + zero = value_pseudo(0); + op = OP_SET_NE; + } return add_binary_op(ep, &bool_ctype, op, src, zero); } @@ -1198,7 +1460,7 @@ static pseudo_t linearize_assignment(struct entrypoint *ep, struct expression *e static const int op_trans[] = { [SPECIAL_ADD_ASSIGN - SPECIAL_BASE] = OP_ADD, [SPECIAL_SUB_ASSIGN - SPECIAL_BASE] = OP_SUB, - [SPECIAL_MUL_ASSIGN - SPECIAL_BASE] = OP_MULU, + [SPECIAL_MUL_ASSIGN - SPECIAL_BASE] = OP_MUL, [SPECIAL_DIV_ASSIGN - SPECIAL_BASE] = OP_DIVU, [SPECIAL_MOD_ASSIGN - SPECIAL_BASE] = OP_MODU, [SPECIAL_SHL_ASSIGN - SPECIAL_BASE] = OP_SHL, @@ -1214,12 +1476,12 @@ static pseudo_t linearize_assignment(struct entrypoint *ep, struct expression *e ctype = src->ctype; oldvalue = cast_pseudo(ep, oldvalue, target->ctype, ctype); - opcode = opcode_sign(op_trans[expr->op - SPECIAL_BASE], ctype); + opcode = map_opcode(op_trans[expr->op - SPECIAL_BASE], ctype); dst = add_binary_op(ep, ctype, opcode, oldvalue, value); + taint_undefined_behaviour(dst->def); value = cast_pseudo(ep, dst, ctype, expr->ctype); } value = linearize_store_gen(ep, value, &ad); - finish_address_gen(ep, &ad); return value; } @@ -1232,35 +1494,25 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi struct symbol *fntype; struct context *context; - if (!expr->ctype) { - warning(expr->pos, "call with no type!"); + if (!expr->ctype) return VOID; - } + fn = expr->fn; + fntype = fn->ctype; + ctype = &fntype->ctype; + if (fntype->type == SYM_NODE) + fntype = fntype->ctype.base_type; + + add_symbol(&insn->fntypes, fntype); FOR_EACH_PTR(expr->args, arg) { pseudo_t new = linearize_expression(ep, arg); use_pseudo(insn, new, add_pseudo(&insn->arguments, new)); + add_symbol(&insn->fntypes, arg->ctype); } END_FOR_EACH_PTR(arg); - fn = expr->fn; - - if (fn->ctype) - ctype = &fn->ctype->ctype; - - fntype = fn->ctype; - if (fntype) { - if (fntype->type == SYM_NODE) - fntype = fntype->ctype.base_type; - } - insn->fntype = fntype; + if (fn->type == EXPR_PREOP && fn->op == '*' && is_func_type(fn->ctype)) + fn = fn->unop; - if (fn->type == EXPR_PREOP) { - if (fn->unop->type == EXPR_SYMBOL) { - struct symbol *sym = fn->unop->symbol; - if (sym->ctype.base_type->type == SYM_FN) - fn = fn->unop; - } - } if (fn->type == EXPR_SYMBOL) { call = symbol_pseudo(ep, fn->symbol); } else { @@ -1304,7 +1556,7 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi static pseudo_t linearize_binop_bool(struct entrypoint *ep, struct expression *expr) { pseudo_t src1, src2, dst; - int op = (expr->op == SPECIAL_LOGICAL_OR) ? OP_OR_BOOL : OP_AND_BOOL; + int op = (expr->op == SPECIAL_LOGICAL_OR) ? OP_OR : OP_AND; src1 = linearize_expression_to_bool(ep, expr->left); src2 = linearize_expression_to_bool(ep, expr->right); @@ -1319,7 +1571,7 @@ static pseudo_t linearize_binop(struct entrypoint *ep, struct expression *expr) pseudo_t src1, src2, dst; static const int opcode[] = { ['+'] = OP_ADD, ['-'] = OP_SUB, - ['*'] = OP_MULU, ['/'] = OP_DIVU, + ['*'] = OP_MUL, ['/'] = OP_DIVU, ['%'] = OP_MODU, ['&'] = OP_AND, ['|'] = OP_OR, ['^'] = OP_XOR, [SPECIAL_LEFTSHIFT] = OP_SHL, @@ -1329,30 +1581,31 @@ static pseudo_t linearize_binop(struct entrypoint *ep, struct expression *expr) src1 = linearize_expression(ep, expr->left); src2 = linearize_expression(ep, expr->right); - op = opcode_sign(opcode[expr->op], expr->ctype); + op = map_opcode(opcode[expr->op], expr->ctype); dst = add_binary_op(ep, expr->ctype, op, src1, src2); + taint_undefined_behaviour(dst->def); return dst; } static pseudo_t linearize_logical_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false); -pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false); +static pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false); static pseudo_t linearize_select(struct entrypoint *ep, struct expression *expr) { - pseudo_t cond, true, false, res; + pseudo_t cond, valt, valf, res; struct instruction *insn; - true = linearize_expression(ep, expr->cond_true); - false = linearize_expression(ep, expr->cond_false); + valt = linearize_expression(ep, expr->cond_true); + valf = linearize_expression(ep, expr->cond_false); cond = linearize_expression(ep, expr->conditional); insn = alloc_typed_instruction(OP_SEL, expr->ctype); if (!expr->cond_true) - true = cond; + valt = cond; use_pseudo(insn, cond, &insn->src1); - use_pseudo(insn, true, &insn->src2); - use_pseudo(insn, false, &insn->src3); + use_pseudo(insn, valt, &insn->src2); + use_pseudo(insn, valf, &insn->src3); res = alloc_pseudo(insn); insn->target = res; @@ -1385,21 +1638,22 @@ static pseudo_t linearize_short_conditional(struct entrypoint *ep, struct expres { pseudo_t src1, src2; struct basic_block *bb_false; - struct basic_block *merge = alloc_basic_block(ep, expr->pos); + struct basic_block *merge; pseudo_t phi1, phi2; - int size = type_size(expr->ctype); if (!expr_false || !ep->active) return VOID; bb_false = alloc_basic_block(ep, expr_false->pos); + merge = alloc_basic_block(ep, expr->pos); + src1 = linearize_expression(ep, cond); - phi1 = alloc_phi(ep->active, src1, size); - add_branch(ep, expr, src1, merge, bb_false); + phi1 = alloc_phi(ep->active, src1, expr->ctype); + add_branch(ep, src1, merge, bb_false); set_activeblock(ep, bb_false); src2 = linearize_expression(ep, expr_false); - phi2 = alloc_phi(ep->active, src2, size); + phi2 = alloc_phi(ep->active, src2, expr->ctype); set_activeblock(ep, merge); return add_join_conditional(ep, expr, phi1, phi2); @@ -1413,7 +1667,6 @@ static pseudo_t linearize_conditional(struct entrypoint *ep, struct expression * pseudo_t src1, src2; pseudo_t phi1, phi2; struct basic_block *bb_true, *bb_false, *merge; - int size = type_size(expr->ctype); if (!cond || !expr_true || !expr_false || !ep->active) return VOID; @@ -1425,26 +1678,65 @@ static pseudo_t linearize_conditional(struct entrypoint *ep, struct expression * set_activeblock(ep, bb_true); src1 = linearize_expression(ep, expr_true); - phi1 = alloc_phi(ep->active, src1, size); + phi1 = alloc_phi(ep->active, src1, expr->ctype); add_goto(ep, merge); set_activeblock(ep, bb_false); src2 = linearize_expression(ep, expr_false); - phi2 = alloc_phi(ep->active, src2, size); + phi2 = alloc_phi(ep->active, src2, expr->ctype); set_activeblock(ep, merge); return add_join_conditional(ep, expr, phi1, phi2); } +static void insert_phis(struct basic_block *bb, pseudo_t src, struct symbol *ctype, + struct instruction *node) +{ + struct basic_block *parent; + + FOR_EACH_PTR(bb->parents, parent) { + struct instruction *br = delete_last_instruction(&parent->insns); + pseudo_t phi = alloc_phi(parent, src, ctype); + add_instruction(&parent->insns, br); + use_pseudo(node, phi, add_pseudo(&node->phi_list, phi)); + } END_FOR_EACH_PTR(parent); +} + static pseudo_t linearize_logical(struct entrypoint *ep, struct expression *expr) { - struct expression *shortcut; + struct symbol *ctype = expr->ctype; + struct basic_block *other, *merge; + struct instruction *node; + pseudo_t src1, src2, phi2; - shortcut = alloc_const_expression(expr->pos, expr->op == SPECIAL_LOGICAL_OR); - shortcut->ctype = expr->ctype; - if (expr->op == SPECIAL_LOGICAL_OR) - return linearize_conditional(ep, expr, expr->left, shortcut, expr->right); - return linearize_conditional(ep, expr, expr->left, expr->right, shortcut); + if (!ep->active || !expr->left || !expr->right) + return VOID; + + other = alloc_basic_block(ep, expr->right->pos); + merge = alloc_basic_block(ep, expr->pos); + node = alloc_phi_node(merge, ctype, NULL); + + // LHS and its shortcut + if (expr->op == SPECIAL_LOGICAL_OR) { + linearize_cond_branch(ep, expr->left, merge, other); + src1 = value_pseudo(1); + } else { + linearize_cond_branch(ep, expr->left, other, merge); + src1 = value_pseudo(0); + } + insert_phis(merge, src1, ctype, node); + + // RHS + set_activeblock(ep, other); + src2 = linearize_expression_to_bool(ep, expr->right); + src2 = cast_pseudo(ep, src2, &bool_ctype, ctype); + phi2 = alloc_phi(ep->active, src2, ctype); + use_pseudo(node, phi2, add_pseudo(&node->phi_list, phi2)); + + // join + set_activeblock(ep, merge); + add_instruction(&merge->insns, node); + return node->target; } static pseudo_t linearize_compare(struct entrypoint *ep, struct expression *expr) @@ -1460,15 +1752,15 @@ static pseudo_t linearize_compare(struct entrypoint *ep, struct expression *expr [SPECIAL_UNSIGNED_LTE] = OP_SET_BE, [SPECIAL_UNSIGNED_GTE] = OP_SET_AE, }; - + int op = opcode_float(cmpop[expr->op], expr->right->ctype); pseudo_t src1 = linearize_expression(ep, expr->left); pseudo_t src2 = linearize_expression(ep, expr->right); - pseudo_t dst = add_binary_op(ep, expr->ctype, cmpop[expr->op], src1, src2); + pseudo_t dst = add_binary_op(ep, expr->ctype, op, src1, src2); return dst; } -pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false) +static pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false) { pseudo_t cond; @@ -1492,7 +1784,7 @@ pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression *expr, s case EXPR_COMPARE: cond = linearize_compare(ep, expr); - add_branch(ep, expr, cond, bb_true, bb_false); + add_branch(ep, cond, bb_true, bb_false); break; case EXPR_PREOP: @@ -1500,8 +1792,8 @@ pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression *expr, s return linearize_cond_branch(ep, expr->unop, bb_false, bb_true); /* fall through */ default: { - cond = linearize_expression(ep, expr); - add_branch(ep, expr, cond, bb_true, bb_false); + cond = linearize_expression_to_bool(ep, expr); + add_branch(ep, cond, bb_true, bb_false); return VOID; } @@ -1536,16 +1828,6 @@ static pseudo_t linearize_cast(struct entrypoint *ep, struct expression *expr) return cast_pseudo(ep, src, orig->ctype, expr->ctype); } -static pseudo_t linearize_position(struct entrypoint *ep, struct expression *pos, struct access_data *ad) -{ - struct expression *init_expr = pos->init_expr; - - ad->offset = pos->init_offset; - ad->source_type = base_type(init_expr->ctype); - ad->result_type = init_expr->ctype; - return linearize_initializer(ep, init_expr, ad); -} - static pseudo_t linearize_initializer(struct entrypoint *ep, struct expression *initializer, struct access_data *ad) { switch (initializer->type) { @@ -1557,12 +1839,12 @@ static pseudo_t linearize_initializer(struct entrypoint *ep, struct expression * break; } case EXPR_POS: - linearize_position(ep, initializer, ad); + ad->offset = initializer->init_offset; + linearize_initializer(ep, initializer->init_expr, ad); break; default: { pseudo_t value = linearize_expression(ep, initializer); - ad->source_type = base_type(initializer->ctype); - ad->result_type = initializer->ctype; + ad->type = initializer->ctype; linearize_store_gen(ep, value, ad); return value; } @@ -1575,14 +1857,12 @@ static void linearize_argument(struct entrypoint *ep, struct symbol *arg, int nr { struct access_data ad = { NULL, }; - ad.source_type = arg; - ad.result_type = arg; + ad.type = arg; ad.address = symbol_pseudo(ep, arg); linearize_store_gen(ep, argument_pseudo(ep, nr), &ad); - finish_address_gen(ep, &ad); } -pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr) +static pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr) { if (!expr) return VOID; @@ -1594,11 +1874,15 @@ pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr) return add_symbol_address(ep, expr->symbol); case EXPR_VALUE: - return value_pseudo(expr->ctype, expr->value); + return value_pseudo(expr->value); - case EXPR_STRING: case EXPR_FVALUE: case EXPR_LABEL: + case EXPR_STRING: + case EXPR_LABEL: return add_setval(ep, expr->ctype, expr); + case EXPR_FVALUE: + return add_setfval(ep, expr->ctype, expr->fvalue); + case EXPR_STATEMENT: return linearize_statement(ep, expr->statement); @@ -1679,16 +1963,12 @@ static pseudo_t linearize_one_symbol(struct entrypoint *ep, struct symbol *sym) // only the existing fields need to be initialized. // FIXME: this init the whole aggregate even if // all fields arelater explicitely initialized. - struct expression *expr = sym->initializer; - ad.pos = expr->pos; - ad.result_type = sym; - ad.source_type = base_type(sym); + ad.type = sym; ad.address = symbol_pseudo(ep, sym); - linearize_store_gen(ep, value_pseudo(sym, 0), &ad); + linearize_store_gen(ep, value_pseudo(0), &ad); } value = linearize_initializer(ep, sym->initializer, &ad); - finish_address_gen(ep, &ad); return value; } @@ -1696,28 +1976,49 @@ static pseudo_t linearize_compound_statement(struct entrypoint *ep, struct state { pseudo_t pseudo; struct statement *s; - struct symbol *ret = stmt->ret; pseudo = VOID; FOR_EACH_PTR(stmt->stmts, s) { pseudo = linearize_statement(ep, s); } END_FOR_EACH_PTR(s); - if (ret) { - struct basic_block *bb = add_label(ep, ret); - struct instruction *phi_node = first_instruction(bb->insns); + return pseudo; +} - if (!phi_node) - return pseudo; +static void add_return(struct entrypoint *ep, struct basic_block *bb, struct symbol *ctype, pseudo_t src) +{ + struct instruction *phi_node = first_instruction(bb->insns); + pseudo_t phi; + if (!phi_node) { + phi_node = alloc_typed_instruction(OP_PHI, ctype); + phi_node->target = alloc_pseudo(phi_node); + phi_node->bb = bb; + add_instruction(&bb->insns, phi_node); + } + phi = alloc_phi(ep->active, src, ctype); + phi->ident = &return_ident; + use_pseudo(phi_node, phi, add_pseudo(&phi_node->phi_list, phi)); +} + +static pseudo_t linearize_fn_statement(struct entrypoint *ep, struct statement *stmt) +{ + struct instruction *phi_node; + struct basic_block *bb; + pseudo_t pseudo; - if (pseudo_list_size(phi_node->phi_list)==1) { - pseudo = first_pseudo(phi_node->phi_list); - assert(pseudo->type == PSEUDO_PHI); - return pseudo->def->src1; + pseudo = linearize_compound_statement(ep, stmt); + if (!is_void_type(stmt->ret)) { // non-void function + struct basic_block *active = ep->active; + if (active && !bb_terminated(active)) { // missing return + struct basic_block *bb_ret; + bb_ret = get_bound_block(ep, stmt->ret); + add_return(ep, bb_ret, stmt->ret, undef_pseudo()); } - return phi_node->target; } - + bb = add_label(ep, stmt->ret); + phi_node = first_instruction(bb->insns); + if (phi_node) + pseudo = phi_node->target; return pseudo; } @@ -1734,14 +2035,16 @@ static pseudo_t linearize_inlined_call(struct entrypoint *ep, struct statement * concat_symbol_list(args->declaration, &ep->syms); FOR_EACH_PTR(args->declaration, sym) { pseudo_t value = linearize_one_symbol(ep, sym); - use_pseudo(insn, value, add_pseudo(&insn->arguments, value)); + add_pseudo(&insn->arguments, value); } END_FOR_EACH_PTR(sym); } - insn->target = pseudo = linearize_compound_statement(ep, stmt); + pseudo = linearize_fn_statement(ep, stmt); + insn->target = pseudo; + use_pseudo(insn, symbol_pseudo(ep, stmt->inline_fn), &insn->func); bb = ep->active; - if (bb && !bb->insns) + if (!bb->insns) bb->pos = stmt->pos; add_one_insn(ep, insn); return pseudo; @@ -1751,12 +2054,8 @@ static pseudo_t linearize_context(struct entrypoint *ep, struct statement *stmt) { struct instruction *insn = alloc_instruction(OP_CONTEXT, 0); struct expression *expr = stmt->expression; - int value = 0; - - if (expr->type == EXPR_VALUE) - value = expr->value; - insn->increment = value; + insn->increment = get_expression_value(expr); insn->context_expr = stmt->context; add_one_insn(ep, insn); return VOID; @@ -1798,7 +2097,6 @@ static void add_asm_output(struct entrypoint *ep, struct instruction *insn, stru if (!expr || !linearize_address_gen(ep, expr, &ad)) return; linearize_store_gen(ep, pseudo, &ad); - finish_address_gen(ep, &ad); rule = __alloc_asm_constraint(0); rule->ident = ident; rule->constraint = constraint; @@ -1808,12 +2106,10 @@ static void add_asm_output(struct entrypoint *ep, struct instruction *insn, stru static pseudo_t linearize_asm_statement(struct entrypoint *ep, struct statement *stmt) { - int state; struct expression *expr; struct instruction *insn; struct asm_rules *rules; const char *constraint; - struct ident *ident; insn = alloc_instruction(OP_ASM, 0); expr = stmt->asm_string; @@ -1827,49 +2123,17 @@ static pseudo_t linearize_asm_statement(struct entrypoint *ep, struct statement insn->asm_rules = rules; /* Gather the inputs.. */ - state = 0; - ident = NULL; - constraint = NULL; FOR_EACH_PTR(stmt->asm_inputs, expr) { - switch (state) { - case 0: /* Identifier */ - state = 1; - ident = (struct ident *)expr; - continue; - - case 1: /* Constraint */ - state = 2; - constraint = expr ? expr->string->data : ""; - continue; - - case 2: /* Expression */ - state = 0; - add_asm_input(ep, insn, expr, constraint, ident); - } + constraint = expr->constraint ? expr->constraint->string->data : ""; + add_asm_input(ep, insn, expr->expr, constraint, expr->name); } END_FOR_EACH_PTR(expr); add_one_insn(ep, insn); /* Assign the outputs */ - state = 0; - ident = NULL; - constraint = NULL; FOR_EACH_PTR(stmt->asm_outputs, expr) { - switch (state) { - case 0: /* Identifier */ - state = 1; - ident = (struct ident *)expr; - continue; - - case 1: /* Constraint */ - state = 2; - constraint = expr ? expr->string->data : ""; - continue; - - case 2: - state = 0; - add_asm_output(ep, insn, expr, constraint, ident); - } + constraint = expr->constraint ? expr->constraint->string->data : ""; + add_asm_output(ep, insn, expr->expr, constraint, expr->name); } END_FOR_EACH_PTR(expr); return VOID; @@ -1916,22 +2180,13 @@ static pseudo_t linearize_declaration(struct entrypoint *ep, struct statement *s static pseudo_t linearize_return(struct entrypoint *ep, struct statement *stmt) { struct expression *expr = stmt->expression; - struct basic_block *bb_return = get_bound_block(ep, stmt->ret_target); + struct symbol *ret = stmt->ret_target; + struct basic_block *bb_return = get_bound_block(ep, ret); struct basic_block *active; pseudo_t src = linearize_expression(ep, expr); active = ep->active; - if (active && src != VOID) { - struct instruction *phi_node = first_instruction(bb_return->insns); - pseudo_t phi; - if (!phi_node) { - phi_node = alloc_typed_instruction(OP_PHI, expr->ctype); - phi_node->target = alloc_pseudo(phi_node); - phi_node->bb = bb_return; - add_instruction(&bb_return->insns, phi_node); - } - phi = alloc_phi(active, src, type_size(expr->ctype)); - phi->ident = &return_ident; - use_pseudo(phi_node, phi, add_pseudo(&phi_node->phi_list, phi)); + if (active && !is_void_type(ret)) { + add_return(ep, bb_return, ret, src); } add_goto(ep, bb_return); return VOID; @@ -1943,16 +2198,20 @@ static pseudo_t linearize_switch(struct entrypoint *ep, struct statement *stmt) struct instruction *switch_ins; struct basic_block *switch_end = alloc_basic_block(ep, stmt->pos); struct basic_block *active, *default_case; + struct expression *expr = stmt->switch_expression; struct multijmp *jmp; pseudo_t pseudo; - pseudo = linearize_expression(ep, stmt->switch_expression); - - active = ep->active; - if (!bb_reachable(active)) + if (!expr || !expr->ctype) return VOID; + pseudo = linearize_expression(ep, expr); + active = ep->active; + if (!active) { + active = alloc_basic_block(ep, stmt->pos); + set_activeblock(ep, active); + } - switch_ins = alloc_instruction(OP_SWITCH, 0); + switch_ins = alloc_typed_instruction(OP_SWITCH, expr->ctype); use_pseudo(switch_ins, pseudo, &switch_ins->cond); add_one_insn(ep, switch_ins); finish_block(ep); @@ -1965,12 +2224,15 @@ static pseudo_t linearize_switch(struct entrypoint *ep, struct statement *stmt) if (!case_stmt->case_expression) { default_case = bb_case; continue; + } else if (case_stmt->case_expression->type != EXPR_VALUE) { + continue; } else { - int begin, end; + struct expression *case_to = case_stmt->case_to; + long long begin, end; begin = end = case_stmt->case_expression->value; - if (case_stmt->case_to) - end = case_stmt->case_to->value; + if (case_to && case_to->type == EXPR_VALUE) + end = case_to->value; if (begin > end) jmp = alloc_multijmp(bb_case, end, begin); else @@ -2047,7 +2309,7 @@ static pseudo_t linearize_iterator(struct entrypoint *ep, struct statement *stmt return VOID; } -pseudo_t linearize_statement(struct entrypoint *ep, struct statement *stmt) +static pseudo_t linearize_statement(struct entrypoint *ep, struct statement *stmt) { struct basic_block *bb; @@ -2124,7 +2386,7 @@ pseudo_t linearize_statement(struct entrypoint *ep, struct statement *stmt) pseudo = linearize_expression(ep, expr); goto_ins = alloc_instruction(OP_COMPUTEDGOTO, 0); - use_pseudo(goto_ins, pseudo, &goto_ins->target); + use_pseudo(goto_ins, pseudo, &goto_ins->src); add_one_insn(ep, goto_ins); FOR_EACH_PTR(stmt->target_list, sym) { @@ -2184,23 +2446,30 @@ pseudo_t linearize_statement(struct entrypoint *ep, struct statement *stmt) static struct entrypoint *linearize_fn(struct symbol *sym, struct symbol *base_type) { + struct statement *stmt = base_type->stmt; struct entrypoint *ep; struct basic_block *bb; + struct symbol *ret_type; struct symbol *arg; struct instruction *entry; + struct instruction *ret; pseudo_t result; int i; - if (!base_type->stmt) + if (!stmt) return NULL; ep = alloc_entrypoint(); - bb = alloc_basic_block(ep, sym->pos); - ep->name = sym; sym->ep = ep; + bb = alloc_basic_block(ep, sym->pos); set_activeblock(ep, bb); + if (stmt->type == STMT_ASM) { // top-level asm + linearize_asm_statement(ep, stmt); + return ep; + } + entry = alloc_instruction(OP_ENTRY, 0); add_one_insn(ep, entry); ep->entry = entry; @@ -2213,67 +2482,14 @@ static struct entrypoint *linearize_fn(struct symbol *sym, struct symbol *base_t linearize_argument(ep, arg, ++i); } END_FOR_EACH_PTR(arg); - result = linearize_statement(ep, base_type->stmt); - if (bb_reachable(ep->active) && !bb_terminated(ep->active)) { - struct symbol *ret_type = base_type->ctype.base_type; - struct instruction *insn = alloc_typed_instruction(OP_RET, ret_type); - - if (type_size(ret_type) > 0) - use_pseudo(insn, result, &insn->src); - add_one_insn(ep, insn); - } - - if (fdump_linearize) { - if (fdump_linearize == 2) - return ep; - show_entry(ep); - } - - /* - * Do trivial flow simplification - branches to - * branches, kill dead basicblocks etc - */ - kill_unreachable_bbs(ep); - - /* - * Turn symbols into pseudos - */ - simplify_symbol_usage(ep); - -repeat: - /* - * Remove trivial instructions, and try to CSE - * the rest. - */ - do { - cleanup_and_cse(ep); - pack_basic_blocks(ep); - } while (repeat_phase & REPEAT_CSE); - - kill_unreachable_bbs(ep); - vrfy_flow(ep); - - /* Cleanup */ - clear_symbol_pseudos(ep); - - /* And track pseudo register usage */ - track_pseudo_liveness(ep); - - /* - * Some flow optimizations can only effectively - * be done when we've done liveness analysis. But - * if they trigger, we need to start all over - * again - */ - if (simplify_flow(ep)) { - clear_liveness(ep); - goto repeat; - } - - /* Finally, add deathnotes to pseudos now that we have them */ - if (dbg_dead) - track_pseudo_death(ep); + result = linearize_fn_statement(ep, stmt); + ret_type = base_type->ctype.base_type; + ret = alloc_typed_instruction(OP_RET, ret_type); + if (type_size(ret_type) > 0) + use_pseudo(ret, result, &ret->src); + add_one_insn(ep, ret); + optimize(ep); return ep; } diff --git a/usr/src/tools/smatch/src/linearize.h b/usr/src/tools/smatch/src/linearize.h index f52eade85f..89da3db6ee 100644 --- a/usr/src/tools/smatch/src/linearize.h +++ b/usr/src/tools/smatch/src/linearize.h @@ -4,11 +4,12 @@ #include "lib.h" #include "allocate.h" #include "token.h" +#include "opcode.h" #include "parse.h" #include "symbol.h" +#include "ptrmap.h" struct instruction; -DECLARE_PTR_LIST(pseudo_ptr_list, pseudo_t); struct pseudo_user { struct instruction *insn; @@ -17,10 +18,12 @@ struct pseudo_user { DECLARE_ALLOCATOR(pseudo_user); DECLARE_PTR_LIST(pseudo_user_list, struct pseudo_user); +DECLARE_PTRMAP(phi_map, struct symbol *, pseudo_t); enum pseudo_type { PSEUDO_VOID, + PSEUDO_UNDEF, PSEUDO_REG, PSEUDO_SYM, PSEUDO_VAL, @@ -30,8 +33,7 @@ enum pseudo_type { struct pseudo { int nr; - int size:16; /* OP_SETVAL only */ - enum pseudo_type type:8; + enum pseudo_type type; struct pseudo_user_list *users; struct ident *ident; union { @@ -46,9 +48,20 @@ extern struct pseudo void_pseudo; #define VOID (&void_pseudo) +static inline bool is_zero(pseudo_t pseudo) +{ + return pseudo->type == PSEUDO_VAL && pseudo->value == 0; +} + +static inline bool is_nonzero(pseudo_t pseudo) +{ + return pseudo->type == PSEUDO_VAL && pseudo->value != 0; +} + + struct multijmp { struct basic_block *target; - int begin, end; + long long begin, end; }; struct asm_constraint { @@ -69,27 +82,29 @@ struct asm_rules { DECLARE_ALLOCATOR(asm_rules); struct instruction { - unsigned opcode:8, + unsigned opcode:7, + tainted:1, size:24; struct basic_block *bb; struct position pos; struct symbol *type; - union { - pseudo_t target; - pseudo_t cond; /* for branch and switch */ - }; + pseudo_t target; union { struct /* entrypoint */ { struct pseudo_list *arg_list; }; struct /* branch */ { + pseudo_t cond; struct basic_block *bb_true, *bb_false; }; struct /* switch */ { + pseudo_t _cond; struct multijmp_list *multijmp_list; }; struct /* phi_node */ { + pseudo_t phi_var; // used for SSA conversion struct pseudo_list *phi_list; + unsigned int used:1; }; struct /* phi source */ { pseudo_t phi_src; @@ -98,7 +113,11 @@ struct instruction { struct /* unops */ { pseudo_t src; struct symbol *orig_type; /* casts */ - unsigned int offset; /* memops */ + }; + struct /* memops */ { + pseudo_t addr; /* alias .src */ + unsigned int offset; + unsigned int is_volatile:1; }; struct /* binops and sel */ { pseudo_t src1, src2, src3; @@ -108,13 +127,15 @@ struct instruction { unsigned from, len; }; struct /* setval */ { - pseudo_t symbol; /* Subtle: same offset as "src" !! */ struct expression *val; }; + struct /* setfval */ { + long double fvalue; + }; struct /* call */ { pseudo_t func; struct pseudo_list *arguments; - struct symbol *fntype; + struct symbol_list *fntypes; }; struct /* context */ { int increment; @@ -128,109 +149,24 @@ struct instruction { }; }; -enum opcode { - OP_BADOP, - - /* Entry */ - OP_ENTRY, - - /* Terminator */ - OP_TERMINATOR, - OP_RET = OP_TERMINATOR, - OP_BR, - OP_CBR, - OP_SWITCH, - OP_INVOKE, - OP_COMPUTEDGOTO, - OP_UNWIND, - OP_TERMINATOR_END = OP_UNWIND, - - /* Binary */ - OP_BINARY, - OP_ADD = OP_BINARY, - OP_SUB, - OP_MULU, OP_MULS, - OP_DIVU, OP_DIVS, - OP_MODU, OP_MODS, - OP_SHL, - OP_LSR, OP_ASR, - - /* Logical */ - OP_AND, - OP_OR, - OP_XOR, - OP_AND_BOOL, - OP_OR_BOOL, - OP_BINARY_END = OP_OR_BOOL, - - /* Binary comparison */ - OP_BINCMP, - OP_SET_EQ = OP_BINCMP, - OP_SET_NE, - OP_SET_LE, - OP_SET_GE, - OP_SET_LT, - OP_SET_GT, - OP_SET_B, - OP_SET_A, - OP_SET_BE, - OP_SET_AE, - OP_BINCMP_END = OP_SET_AE, - - /* Uni */ - OP_NOT, - OP_NEG, - - /* Select - three input values */ - OP_SEL, - - /* Memory */ - OP_MALLOC, - OP_FREE, - OP_ALLOCA, - OP_LOAD, - OP_STORE, - OP_SETVAL, - OP_SYMADDR, - OP_GET_ELEMENT_PTR, - - /* Other */ - OP_PHI, - OP_PHISOURCE, - OP_CAST, - OP_SCAST, - OP_FPCAST, - OP_PTRCAST, - OP_INLINED_CALL, - OP_CALL, - OP_VANEXT, - OP_VAARG, - OP_SLICE, - OP_SNOP, - OP_LNOP, - OP_NOP, - OP_DEATHNOTE, - OP_ASM, - - /* Sparse tagging (line numbers, context, whatever) */ - OP_CONTEXT, - OP_RANGE, - - /* Needed to translate SSA back to normal form */ - OP_COPY, -}; - struct basic_block_list; struct instruction_list; struct basic_block { struct position pos; unsigned long generation; - int context; + union { + int context; + int postorder_nr; /* postorder number */ + int dom_level; /* level in the dominance tree */ + }; struct entrypoint *ep; struct basic_block_list *parents; /* sources */ struct basic_block_list *children; /* destinations */ struct instruction_list *insns; /* Linear list of instructions */ + struct basic_block *idom; /* link to the immediate dominator */ + struct basic_block_list *doms; /* list of BB idominated by this one */ + struct phi_map *phi_map; struct pseudo_list *needs, *defines; union { unsigned int nr; /* unique id for label's names */ @@ -239,6 +175,14 @@ struct basic_block { }; +// +// return the opcode of the instruction defining ``SRC`` if existing +// and OP_BADOP if not. It also assigns the defining instruction +// to ``DEF``. +#define DEF_OPCODE(DEF, SRC) \ + (((SRC)->type == PSEUDO_REG && (DEF = (SRC)->def)) ? DEF->opcode : OP_BADOP) + + static inline void add_bb(struct basic_block_list **list, struct basic_block *bb) { add_ptr_list(list, bb); @@ -264,6 +208,11 @@ static inline int remove_pseudo(struct pseudo_list **list, pseudo_t pseudo) return delete_ptr_list_entry((struct ptr_list **)list, pseudo, 0) != 0; } +static inline int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo) +{ + return lookup_ptr_list_entry((struct ptr_list *)list, pseudo); +} + static inline int bb_terminated(struct basic_block *bb) { struct instruction *insn; @@ -279,11 +228,12 @@ static inline int bb_reachable(struct basic_block *bb) return bb != NULL; } -static inline void add_pseudo_ptr(pseudo_t *ptr, struct pseudo_ptr_list **list) +static inline int lookup_bb(struct basic_block_list *list, struct basic_block *bb) { - add_ptr_list(list, ptr); + return lookup_ptr_list_entry((struct ptr_list *)list, bb); } + static inline void add_pseudo_user_ptr(struct pseudo_user *user, struct pseudo_user_list **list) { add_ptr_list(list, user); @@ -291,7 +241,32 @@ static inline void add_pseudo_user_ptr(struct pseudo_user *user, struct pseudo_u static inline int has_use_list(pseudo_t p) { - return (p && p->type != PSEUDO_VOID && p->type != PSEUDO_VAL); + return (p && p->type != PSEUDO_VOID && p->type != PSEUDO_UNDEF && p->type != PSEUDO_VAL); +} + +static inline int pseudo_user_list_size(struct pseudo_user_list *list) +{ + return ptr_list_size((struct ptr_list *)list); +} + +static inline bool pseudo_user_list_empty(struct pseudo_user_list *list) +{ + return ptr_list_empty((struct ptr_list *)list); +} + +static inline int has_users(pseudo_t p) +{ + return !pseudo_user_list_empty(p->users); +} + +static inline bool multi_users(pseudo_t p) +{ + return ptr_list_multiple((struct ptr_list *)(p->users)); +} + +static inline int nbr_users(pseudo_t p) +{ + return pseudo_user_list_size(p->users); } static inline struct pseudo_user *alloc_pseudo_user(struct instruction *insn, pseudo_t *pp) @@ -327,15 +302,21 @@ struct entrypoint { struct basic_block_list *bbs; struct basic_block *active; struct instruction *entry; + unsigned int dom_levels; /* max levels in the dom tree */ }; extern void insert_select(struct basic_block *bb, struct instruction *br, struct instruction *phi, pseudo_t if_true, pseudo_t if_false); extern void insert_branch(struct basic_block *bb, struct instruction *br, struct basic_block *target); -pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, int size); +struct instruction *alloc_phisrc(pseudo_t pseudo, struct symbol *type); +struct instruction *alloc_phi_node(struct basic_block *bb, struct symbol *type, struct ident *ident); +struct instruction *insert_phi_node(struct basic_block *bb, struct symbol *var); +void add_phi_node(struct basic_block *bb, struct instruction *phi_node); + +pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, struct symbol *type); pseudo_t alloc_pseudo(struct instruction *def); -pseudo_t value_pseudo(struct symbol *type, long long val); -unsigned int value_size(long long value); +pseudo_t value_pseudo(long long val); +pseudo_t undef_pseudo(void); struct entrypoint *linearize_symbol(struct symbol *sym); int unssa(struct entrypoint *ep); @@ -343,6 +324,7 @@ void show_entry(struct entrypoint *ep); const char *show_pseudo(pseudo_t pseudo); void show_bb(struct basic_block *bb); const char *show_instruction(struct instruction *insn); +const char *show_label(struct basic_block *bb); #endif /* LINEARIZE_H */ diff --git a/usr/src/tools/smatch/src/liveness.c b/usr/src/tools/smatch/src/liveness.c index 7461738b47..93a7cc3003 100644 --- a/usr/src/tools/smatch/src/liveness.c +++ b/usr/src/tools/smatch/src/liveness.c @@ -7,6 +7,7 @@ #include <assert.h> +#include "liveness.h" #include "parse.h" #include "expression.h" #include "linearize.h" @@ -53,6 +54,7 @@ static void track_instruction_usage(struct basic_block *bb, struct instruction * switch (insn->opcode) { case OP_RET: + case OP_COMPUTEDGOTO: USES(src); break; @@ -61,18 +63,16 @@ static void track_instruction_usage(struct basic_block *bb, struct instruction * USES(cond); break; - case OP_COMPUTEDGOTO: - USES(target); - break; - /* Binary */ case OP_BINARY ... OP_BINARY_END: + case OP_FPCMP ... OP_FPCMP_END: case OP_BINCMP ... OP_BINCMP_END: USES(src1); USES(src2); DEFINES(target); break; /* Uni */ - case OP_NOT: case OP_NEG: + case OP_UNOP ... OP_UNOP_END: + case OP_SYMADDR: USES(src1); DEFINES(target); break; @@ -90,13 +90,10 @@ static void track_instruction_usage(struct basic_block *bb, struct instruction * break; case OP_SETVAL: + case OP_SETFVAL: DEFINES(target); break; - case OP_SYMADDR: - USES(symbol); DEFINES(target); - break; - /* Other */ case OP_PHI: /* Phi-nodes are "backwards" nodes. Their def doesn't matter */ @@ -111,13 +108,6 @@ static void track_instruction_usage(struct basic_block *bb, struct instruction * USES(phi_src); break; - case OP_CAST: - case OP_SCAST: - case OP_FPCAST: - case OP_PTRCAST: - USES(src); DEFINES(target); - break; - case OP_CALL: USES(func); if (insn->target != VOID) @@ -140,31 +130,12 @@ static void track_instruction_usage(struct basic_block *bb, struct instruction * break; case OP_BADOP: - case OP_INVOKE: - case OP_UNWIND: - case OP_MALLOC: - case OP_FREE: - case OP_ALLOCA: - case OP_GET_ELEMENT_PTR: - case OP_VANEXT: - case OP_VAARG: - case OP_SNOP: - case OP_LNOP: case OP_NOP: case OP_CONTEXT: break; } } -int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo) -{ - pseudo_t old; - FOR_EACH_PTR(list,old) { - if (old == pseudo) - return 1; - } END_FOR_EACH_PTR(old); - return 0; -} static int liveness_changed; @@ -276,7 +247,7 @@ static void merge_pseudo_list(struct pseudo_list *src, struct pseudo_list **dest } END_FOR_EACH_PTR(pseudo); } -void track_phi_uses(struct instruction *insn) +static void track_phi_uses(struct instruction *insn) { pseudo_t phi; FOR_EACH_PTR(insn->phi_list, phi) { diff --git a/usr/src/tools/smatch/src/liveness.h b/usr/src/tools/smatch/src/liveness.h new file mode 100644 index 0000000000..882167cee8 --- /dev/null +++ b/usr/src/tools/smatch/src/liveness.h @@ -0,0 +1,11 @@ +#ifndef LIVENESS_H +#define LIVENESS_H + +struct entrypoint; + +/* liveness.c */ +void clear_liveness(struct entrypoint *ep); +void track_pseudo_liveness(struct entrypoint *ep); +void track_pseudo_death(struct entrypoint *ep); + +#endif diff --git a/usr/src/tools/smatch/src/machine.h b/usr/src/tools/smatch/src/machine.h new file mode 100644 index 0000000000..b46383ac1d --- /dev/null +++ b/usr/src/tools/smatch/src/machine.h @@ -0,0 +1,83 @@ +#ifndef MACHINE_H +#define MACHINE_H + +#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define ARCH_BIG_ENDIAN 1 +#else +#define ARCH_BIG_ENDIAN 0 +#endif + + +enum { + ARCH_LP32, + ARCH_X32, + ARCH_LP64, + ARCH_LLP64, +}; + +#ifdef __LP64__ +#define ARCH_M64_DEFAULT ARCH_LP64 +#elif defined(__x86_64__) || defined(__x86_64) +#define ARCH_M64_DEFAULT ARCH_X32 +#else +#define ARCH_M64_DEFAULT ARCH_LP32 +#endif + + +enum machine { + MACH_ARM, + MACH_ARM64, + MACH_I386, + MACH_X86_64, + MACH_MIPS32, + MACH_MIPS64, + MACH_PPC32, + MACH_PPC64, + MACH_RISCV32, + MACH_RISCV64, + MACH_SPARC32, + MACH_SPARC64, + MACH_M68K, + MACH_S390X, + MACH_UNKNOWN +}; + +#if defined(__aarch64__) +#define MACH_NATIVE MACH_ARM64 +#elif defined(__arm__) +#define MACH_NATIVE MACH_ARM +#elif defined(__x86_64__) || defined(__x86_64) +#define MACH_NATIVE MACH_X86_64 +#elif defined(__i386__) || defined(__i386) +#define MACH_NATIVE MACH_I386 +#elif defined(__mips64__) || (defined(__mips) && __mips == 64) +#define MACH_NATIVE MACH_MIPS64 +#elif defined(__mips__) || defined(__mips) +#define MACH_NATIVE MACH_MIPS32 +#elif defined(__powerpc64__) || defined(__ppc64__) +#define MACH_NATIVE MACH_PPC64 +#elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) +#define MACH_NATIVE MACH_PPC32 +#elif defined(__riscv) && (__riscv_xlen == 64) +#define MACH_NATIVE MACH_RISCV64 +#elif defined(__riscv) && (__riscv_xlen == 32) +#define MACH_NATIVE MACH_RISCV32 +#elif defined(__sparc_v9__) +#define MACH_NATIVE MACH_SPARC64 +#elif defined(__sparc__) || defined(__sparc) +#define MACH_NATIVE MACH_SPARC32 +#elif defined(__m68k__) +#define MACH_NATIVE MACH_M68K +#elif defined(__s390x__) || defined(__zarch__) +#define MACH_NATIVE MACH_S390X +#else +#define MACH_NATIVE MACH_UNKNOWN +#endif + +#if defined(__CHAR_UNSIGNED__) +#define UNSIGNED_CHAR 1 +#else +#define UNSIGNED_CHAR 0 +#endif + +#endif diff --git a/usr/src/tools/smatch/src/macro_table.c b/usr/src/tools/smatch/src/macro_table.c index 18c5538ed4..251bd99f71 100644 --- a/usr/src/tools/smatch/src/macro_table.c +++ b/usr/src/tools/smatch/src/macro_table.c @@ -31,14 +31,14 @@ static struct hashtable *macro_table; -static DEFINE_HASHTABLE_INSERT(do_insert_macro, struct position, char); -static DEFINE_HASHTABLE_SEARCH(do_search_macro, struct position, char); +static DEFINE_HASHTABLE_INSERT(do_insert_macro, struct position, struct string_list); +static DEFINE_HASHTABLE_SEARCH(do_search_macro, struct position, struct string_list); static inline unsigned int position_hash(void *_pos) { struct position *pos = _pos; - return pos->line | (pos->pos << 22) | (pos->stream << 18); + return pos->line | (pos->pos << 22) | (pos->stream << 18); } static inline int equalkeys(void *_pos1, void *_pos2) @@ -50,18 +50,47 @@ static inline int equalkeys(void *_pos1, void *_pos2) pos1->stream == pos2->stream; } +static void insert_macro_string(struct string_list **str_list, char *new) +{ + add_ptr_list(str_list, new); +} + void store_macro_pos(struct token *token) { + struct string_list *list; + if (!macro_table) macro_table = create_hashtable(5000, position_hash, equalkeys); - if (get_macro_name(token->pos)) - return; + list = do_search_macro(macro_table, &token->pos); + insert_macro_string(&list, token->ident->name); - do_insert_macro(macro_table, &token->pos, token->ident->name); + do_insert_macro(macro_table, &token->pos, list); } char *get_macro_name(struct position pos) { + struct string_list *list; + + if (!macro_table) + return NULL; + list = do_search_macro(macro_table, &pos); + return first_ptr_list((struct ptr_list *)list); +} + +char *get_inner_macro(struct position pos) +{ + struct string_list *list; + + if (!macro_table) + return NULL; + list = do_search_macro(macro_table, &pos); + return last_ptr_list((struct ptr_list *)list); +} + +struct string_list *get_all_macros(struct position pos) +{ + if (!macro_table) + return NULL; return do_search_macro(macro_table, &pos); } diff --git a/usr/src/tools/smatch/src/memops.c b/usr/src/tools/smatch/src/memops.c index 6a795c19c4..5df2c03394 100644 --- a/usr/src/tools/smatch/src/memops.c +++ b/usr/src/tools/smatch/src/memops.c @@ -54,7 +54,7 @@ no_dominance: found_dominator: br = delete_last_instruction(&parent->insns); - phi = alloc_phi(parent, one->target, one->size); + phi = alloc_phi(parent, one->target, one->type); phi->ident = phi->ident ? : one->target->ident; add_instruction(&parent->insns, br); use_pseudo(insn, phi, add_pseudo(dominators, phi)); @@ -69,6 +69,8 @@ static int address_taken(pseudo_t pseudo) struct instruction *insn = pu->insn; if (insn->bb && (insn->opcode != OP_LOAD && insn->opcode != OP_STORE)) return 1; + if (pu->userp != &insn->src) + return 1; } END_FOR_EACH_PTR(pu); return 0; } @@ -97,7 +99,7 @@ static void simplify_loads(struct basic_block *bb) /* Check for illegal offsets.. */ check_access(insn); - if (insn->type->ctype.modifiers & MOD_VOLATILE) + if (insn->is_volatile) continue; RECURSE_PTR_REVERSE(insn, dom) { @@ -127,11 +129,16 @@ static void simplify_loads(struct basic_block *bb) if (!dominators) { if (local) { assert(pseudo->type != PSEUDO_ARG); - convert_load_instruction(insn, value_pseudo(insn->type, 0)); + convert_load_instruction(insn, value_pseudo(0)); } goto next_load; } rewrite_load_instruction(insn, dominators); + } else { // cleanup pending phi-sources + pseudo_t phi; + FOR_EACH_PTR(dominators, phi) { + kill_instruction(phi->def); + } END_FOR_EACH_PTR(phi); } } next_load: @@ -139,15 +146,6 @@ next_load: } END_FOR_EACH_PTR_REVERSE(insn); } -static void kill_store(struct instruction *insn) -{ - if (insn) { - insn->bb = NULL; - insn->opcode = OP_SNOP; - kill_use(&insn->target); - } -} - static void kill_dominated_stores(struct basic_block *bb) { struct instruction *insn; @@ -158,8 +156,14 @@ static void kill_dominated_stores(struct basic_block *bb) if (insn->opcode == OP_STORE) { struct instruction *dom; pseudo_t pseudo = insn->src; - int local = local_pseudo(pseudo); + int local; + if (!insn->type) + continue; + if (insn->is_volatile) + continue; + + local = local_pseudo(pseudo); RECURSE_PTR_REVERSE(insn, dom) { int dominance; if (!dom->bb) @@ -172,7 +176,7 @@ static void kill_dominated_stores(struct basic_block *bb) if (dom->opcode == OP_LOAD) goto next_store; /* Yeehaa! Found one! */ - kill_store(dom); + kill_instruction_force(dom); } } END_FOR_EACH_PTR_REVERSE(dom); @@ -186,6 +190,7 @@ next_store: void simplify_memops(struct entrypoint *ep) { struct basic_block *bb; + pseudo_t pseudo; FOR_EACH_PTR_REVERSE(ep->bbs, bb) { simplify_loads(bb); @@ -194,4 +199,15 @@ void simplify_memops(struct entrypoint *ep) FOR_EACH_PTR_REVERSE(ep->bbs, bb) { kill_dominated_stores(bb); } END_FOR_EACH_PTR_REVERSE(bb); + + FOR_EACH_PTR(ep->accesses, pseudo) { + struct symbol *var = pseudo->sym; + unsigned long mod; + if (!var) + continue; + mod = var->ctype.modifiers; + if (mod & (MOD_VOLATILE | MOD_NONLOCAL | MOD_STATIC)) + continue; + kill_dead_stores(ep, pseudo, local_pseudo(pseudo)); + } END_FOR_EACH_PTR(pseudo); } diff --git a/usr/src/tools/smatch/src/obfuscate.c b/usr/src/tools/smatch/src/obfuscate.c index 18cbd0eef7..998eda643c 100644 --- a/usr/src/tools/smatch/src/obfuscate.c +++ b/usr/src/tools/smatch/src/obfuscate.c @@ -69,8 +69,8 @@ int main(int argc, char **argv) char *file; emit_symbol_list(sparse_initialize(argc, argv, &filelist)); - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { emit_symbol_list(sparse(file)); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); return 0; } diff --git a/usr/src/tools/smatch/src/opcode.c b/usr/src/tools/smatch/src/opcode.c new file mode 100644 index 0000000000..98ad768f87 --- /dev/null +++ b/usr/src/tools/smatch/src/opcode.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 Luc Van Oostenryck + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "opcode.h" + +const struct opcode_table opcode_table[OP_LAST] = { +#define OPCODE(OP,NG,SW,TF,N,FL) \ + [OP_##OP] = { \ + .negate = OP_##NG, \ + .swap = OP_##SW, \ + .to_float = OP_##TF, \ + .arity = N, \ + .flags = FL, \ + }, +#define OPCODE_RANGE(OP,S,E) +#include "opcode.def" +#undef OPCODE +#undef OPCODE_RANGE +}; diff --git a/usr/src/tools/smatch/src/opcode.def b/usr/src/tools/smatch/src/opcode.def new file mode 100644 index 0000000000..57d827f449 --- /dev/null +++ b/usr/src/tools/smatch/src/opcode.def @@ -0,0 +1,114 @@ +// OPCODE negated swaped float arity, flags + +OPCODE(BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE) + +/* Entry */ +OPCODE(ENTRY, BADOP, BADOP, BADOP, 0, OPF_NONE) + +/* Terminator */ +OPCODE(RET, BADOP, BADOP, BADOP, 1, OPF_NONE) +OPCODE(BR, BADOP, BADOP, BADOP, 0, OPF_NONE) +OPCODE(CBR, BADOP, BADOP, BADOP, 1, OPF_NONE) +OPCODE(SWITCH, BADOP, BADOP, BADOP, 1, OPF_NONE) +OPCODE(COMPUTEDGOTO, BADOP, BADOP, BADOP, 1, OPF_NONE) +OPCODE_RANGE(TERMINATOR, RET, COMPUTEDGOTO) + +/* Binary */ +OPCODE(ADD, BADOP, BADOP, FADD, 2, OPF_TARGET) +OPCODE(SUB, BADOP, BADOP, FSUB, 2, OPF_TARGET) +OPCODE(MUL, BADOP, BADOP, FMUL, 2, OPF_TARGET) +OPCODE(DIVU, BADOP, BADOP, FDIV, 2, OPF_TARGET) +OPCODE(DIVS, BADOP, BADOP, FDIV, 2, OPF_TARGET) +OPCODE(MODU, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE(MODS, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE(SHL, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE(LSR, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE(ASR, BADOP, BADOP, BADOP, 2, OPF_TARGET) + +/* Floating-point binops */ +OPCODE(FADD, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE(FSUB, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE(FMUL, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE(FDIV, BADOP, BADOP, BADOP, 2, OPF_TARGET) + +/* Logical */ +OPCODE(AND_BOOL, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE(OR_BOOL, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE(AND, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE(OR, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE(XOR, BADOP, BADOP, BADOP, 2, OPF_TARGET) +OPCODE_RANGE(BINARY, ADD, XOR) + +/* floating-point comparison */ +OPCODE(FCMP_ORD, FCMP_UNO, FCMP_ORD, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_OEQ, FCMP_UNE, FCMP_OEQ, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_ONE, FCMP_UEQ, FCMP_ONE, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_UEQ, FCMP_ONE, FCMP_UEQ, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_UNE, FCMP_OEQ, FCMP_UNE, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_OLT, FCMP_UGE, FCMP_OGT, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_OLE, FCMP_UGT, FCMP_OGE, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_OGE, FCMP_ULT, FCMP_OLE, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_OGT, FCMP_ULE, FCMP_OLT, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_ULT, FCMP_OGE, FCMP_UGT, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_ULE, FCMP_OGT, FCMP_UGE, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_UGE, FCMP_OLT, FCMP_ULE, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_UGT, FCMP_OLE, FCMP_ULT, BADOP, 2, OPF_TARGET) +OPCODE(FCMP_UNO, FCMP_ORD, FCMP_UNO, BADOP, 2, OPF_TARGET) +OPCODE_RANGE(FPCMP, FCMP_ORD, FCMP_UNO) + +/* Binary comparison */ +OPCODE(SET_EQ, SET_NE, SET_EQ, FCMP_OEQ, 2, OPF_TARGET) +OPCODE(SET_LT, SET_GE, SET_GT, FCMP_OLT, 2, OPF_TARGET) +OPCODE(SET_LE, SET_GT, SET_GE, FCMP_OLE, 2, OPF_TARGET) +OPCODE(SET_GE, SET_LT, SET_LE, FCMP_OGE, 2, OPF_TARGET) +OPCODE(SET_GT, SET_LE, SET_LT, FCMP_OGT, 2, OPF_TARGET) +OPCODE(SET_B, SET_AE, SET_A, FCMP_OLT, 2, OPF_TARGET) +OPCODE(SET_BE, SET_A, SET_AE, FCMP_OLE, 2, OPF_TARGET) +OPCODE(SET_AE, SET_B, SET_BE, FCMP_OGE, 2, OPF_TARGET) +OPCODE(SET_A, SET_BE, SET_B, FCMP_OGT, 2, OPF_TARGET) +OPCODE(SET_NE, SET_EQ, SET_NE, FCMP_UNE, 2, OPF_TARGET) +OPCODE_RANGE(BINCMP, SET_EQ, SET_NE) + +/* Uni */ +OPCODE(NOT, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(NEG, BADOP, BADOP, FNEG, 1, OPF_TARGET) +OPCODE(FNEG, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(TRUNC, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(ZEXT, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(SEXT, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(FCVTU, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(FCVTS, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(UCVTF, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(SCVTF, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(FCVTF, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(UTPTR, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(PTRTU, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(PTRCAST, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE_RANGE(UNOP, NOT, PTRCAST) +OPCODE(SYMADDR, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(SLICE, BADOP, BADOP, BADOP, 1, OPF_TARGET) + +/* Select - three input values */ +OPCODE(SEL, BADOP, BADOP, BADOP, 3, OPF_TARGET) + +/* Memory */ +OPCODE(LOAD, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(STORE, BADOP, BADOP, BADOP, 1, OPF_NONE) + +/* Other */ +OPCODE(PHISOURCE, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(PHI, BADOP, BADOP, BADOP, 0, OPF_TARGET) +OPCODE(SETVAL, BADOP, BADOP, BADOP, 0, OPF_TARGET) +OPCODE(SETFVAL, BADOP, BADOP, BADOP, 0, OPF_TARGET) +OPCODE(CALL, BADOP, BADOP, BADOP, 1, OPF_TARGET) +OPCODE(INLINED_CALL, BADOP, BADOP, BADOP, 0, OPF_NONE) +OPCODE(NOP, BADOP, BADOP, BADOP, 0, OPF_NONE) +OPCODE(DEATHNOTE, BADOP, BADOP, BADOP, 0, OPF_NONE) +OPCODE(ASM, BADOP, BADOP, BADOP, 0, OPF_NONE) + +/* Sparse tagging (line numbers, context, whatever) */ +OPCODE(CONTEXT, BADOP, BADOP, BADOP, 0, OPF_NONE) +OPCODE(RANGE, BADOP, BADOP, BADOP, 3, OPF_NONE) + +/* Needed to translate SSA back to normal form */ +OPCODE(COPY, BADOP, BADOP, BADOP, 1, OPF_TARGET) diff --git a/usr/src/tools/smatch/src/opcode.h b/usr/src/tools/smatch/src/opcode.h new file mode 100644 index 0000000000..e426bed4f7 --- /dev/null +++ b/usr/src/tools/smatch/src/opcode.h @@ -0,0 +1,33 @@ +#ifndef OPCODE_H +#define OPCODE_H + +#include "symbol.h" + +enum opcode { +#define OPCODE(OP,NG,SW,TF,N,FL) OP_##OP, +#define OPCODE_RANGE(OP,S,E) OP_##OP = OP_##S, OP_##OP##_END = OP_##E, +#include "opcode.def" +#undef OPCODE +#undef OPCODE_RANGE + OP_LAST, /* keep this one last! */ +}; + +extern const struct opcode_table { + int negate:8; + int swap:8; + int to_float:8; + unsigned int arity:2; + unsigned int flags:6; +#define OPF_NONE 0 +#define OPF_TARGET (1 << 0) +} opcode_table[]; + + +static inline int opcode_float(int opcode, struct symbol *type) +{ + if (!type || !is_float_type(type)) + return opcode; + return opcode_table[opcode].to_float; +} + +#endif diff --git a/usr/src/tools/smatch/src/optimize.c b/usr/src/tools/smatch/src/optimize.c new file mode 100644 index 0000000000..e8cb7fc31e --- /dev/null +++ b/usr/src/tools/smatch/src/optimize.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT +// +// optimize.c - main optimization loop +// +// Copyright (C) 2004 Linus Torvalds +// Copyright (C) 2004 Christopher Li + +#include <assert.h> +#include "optimize.h" +#include "flowgraph.h" +#include "linearize.h" +#include "liveness.h" +#include "flow.h" +#include "cse.h" +#include "ir.h" +#include "ssa.h" + +int repeat_phase; + +static void clear_symbol_pseudos(struct entrypoint *ep) +{ + pseudo_t pseudo; + + FOR_EACH_PTR(ep->accesses, pseudo) { + pseudo->sym->pseudo = NULL; + } END_FOR_EACH_PTR(pseudo); +} + + +static void clean_up_insns(struct entrypoint *ep) +{ + struct basic_block *bb; + + FOR_EACH_PTR(ep->bbs, bb) { + struct instruction *insn; + FOR_EACH_PTR(bb->insns, insn) { + repeat_phase |= simplify_instruction(insn); + if (!insn->bb) + continue; + assert(insn->bb == bb); + cse_collect(insn); + } END_FOR_EACH_PTR(insn); + } END_FOR_EACH_PTR(bb); +} + +void optimize(struct entrypoint *ep) +{ + if (fdump_ir & PASS_LINEARIZE) + show_entry(ep); + + /* + * Do trivial flow simplification - branches to + * branches, kill dead basicblocks etc + */ + kill_unreachable_bbs(ep); + ir_validate(ep); + + domtree_build(ep); + + /* + * Turn symbols into pseudos + */ + if (fpasses & PASS_MEM2REG) + ssa_convert(ep); + ir_validate(ep); + if (fdump_ir & PASS_MEM2REG) + show_entry(ep); + + if (!(fpasses & PASS_OPTIM)) + return; +repeat: + /* + * Remove trivial instructions, and try to CSE + * the rest. + */ + do { + simplify_memops(ep); + //ir_validate(ep); + do { + repeat_phase = 0; + clean_up_insns(ep); + if (repeat_phase & REPEAT_CFG_CLEANUP) + kill_unreachable_bbs(ep); + + cse_eliminate(ep); + + if (repeat_phase & REPEAT_SYMBOL_CLEANUP) + simplify_memops(ep); + //ir_validate(ep); + } while (repeat_phase); + pack_basic_blocks(ep); + //ir_validate(ep); + if (repeat_phase & REPEAT_CFG_CLEANUP) + kill_unreachable_bbs(ep); + //ir_validate(ep); + } while (repeat_phase); + //ir_validate(ep); + + vrfy_flow(ep); + + /* Cleanup */ + clear_symbol_pseudos(ep); + + /* And track pseudo register usage */ + track_pseudo_liveness(ep); + + /* + * Some flow optimizations can only effectively + * be done when we've done liveness analysis. But + * if they trigger, we need to start all over + * again + */ + if (simplify_flow(ep)) { + //ir_validate(ep); + clear_liveness(ep); + if (repeat_phase & REPEAT_CFG_CLEANUP) + kill_unreachable_bbs(ep); + goto repeat; + } + //ir_validate(ep); + + /* Finally, add deathnotes to pseudos now that we have them */ + if (dbg_dead) + track_pseudo_death(ep); +} diff --git a/usr/src/tools/smatch/src/optimize.h b/usr/src/tools/smatch/src/optimize.h new file mode 100644 index 0000000000..31e2cf0817 --- /dev/null +++ b/usr/src/tools/smatch/src/optimize.h @@ -0,0 +1,9 @@ +#ifndef OPTIMIZE_H +#define OPTIMIZE_H + +struct entrypoint; + +/* optimize.c */ +void optimize(struct entrypoint *ep); + +#endif diff --git a/usr/src/tools/smatch/src/parse.c b/usr/src/tools/smatch/src/parse.c index 44c5970732..44f32bbec2 100644 --- a/usr/src/tools/smatch/src/parse.c +++ b/usr/src/tools/smatch/src/parse.c @@ -58,6 +58,8 @@ static declarator_t typedef_specifier, inline_specifier, auto_specifier, register_specifier, static_specifier, extern_specifier, thread_specifier, const_qualifier, volatile_qualifier; +static declarator_t restrict_qualifier; +static declarator_t atomic_qualifier; static struct token *parse_if_statement(struct token *token, struct statement *stmt); static struct token *parse_return_statement(struct token *token, struct statement *stmt); @@ -80,6 +82,7 @@ typedef struct token *attr_t(struct token *, struct symbol *, static attr_t attribute_packed, attribute_aligned, attribute_modifier, + attribute_ext_visible, attribute_bitwise, attribute_address_space, attribute_context, attribute_designated_init, @@ -89,7 +92,8 @@ static attr_t typedef struct symbol *to_mode_t(struct symbol *); static to_mode_t - to_QI_mode, to_HI_mode, to_SI_mode, to_DI_mode, to_TI_mode, to_word_mode; + to_QI_mode, to_HI_mode, to_SI_mode, to_DI_mode, to_TI_mode; +static to_mode_t to_pointer_mode, to_word_mode; enum { Set_T = 1, @@ -115,6 +119,23 @@ enum { SNone = 0, STypedef, SAuto, SRegister, SExtern, SStatic, SForced, SMax, }; +static void asm_modifier(struct token *token, unsigned long *mods, unsigned long mod) +{ + if (*mods & mod) + warning(token->pos, "duplicated asm modifier"); + *mods |= mod; +} + +static void asm_modifier_volatile(struct token *token, unsigned long *mods) +{ + asm_modifier(token, mods, MOD_VOLATILE); +} + +static void asm_modifier_inline(struct token *token, unsigned long *mods) +{ + asm_modifier(token, mods, MOD_INLINE); +} + static struct symbol_op typedef_op = { .type = KW_MODIFIER, .declarator = typedef_specifier, @@ -123,6 +144,7 @@ static struct symbol_op typedef_op = { static struct symbol_op inline_op = { .type = KW_MODIFIER, .declarator = inline_specifier, + .asm_modifier = asm_modifier_inline, }; static declarator_t noreturn_specifier; @@ -170,10 +192,17 @@ static struct symbol_op const_op = { static struct symbol_op volatile_op = { .type = KW_QUALIFIER, .declarator = volatile_qualifier, + .asm_modifier = asm_modifier_volatile, }; static struct symbol_op restrict_op = { .type = KW_QUALIFIER, + .declarator = restrict_qualifier, +}; + +static struct symbol_op atomic_op = { + .type = KW_QUALIFIER, + .declarator = atomic_qualifier, }; static struct symbol_op typeof_op = { @@ -235,14 +264,6 @@ static struct symbol_op double_op = { .class = CReal, }; -/* FIXME: this is not even slightly right. */ -static struct symbol_op complex_op = { - .type = KW_SPECIFIER, - .test = 0, //Set_T|Set_Signed|Set_Unsigned|Set_Short|Set_Vlong, - .set = 0, //Set_Double, //Set_T,Set_Double, - .class = CReal, -}; - static struct symbol_op float_op = { .type = KW_SPECIFIER | KW_SHORT, .test = Set_T|Set_Signed|Set_Unsigned|Set_Short|Set_Long, @@ -353,6 +374,10 @@ static struct symbol_op attr_mod_op = { .attribute = attribute_modifier, }; +static struct symbol_op ext_visible_op = { + .attribute = attribute_ext_visible, +}; + static struct symbol_op attr_bitwise_op = { .attribute = attribute_bitwise, }; @@ -410,6 +435,11 @@ static struct symbol_op mode_TI_op = { .to_mode = to_TI_mode }; +static struct symbol_op mode_pointer_op = { + .type = KW_MODE, + .to_mode = to_pointer_mode +}; + static struct symbol_op mode_word_op = { .type = KW_MODE, .to_mode = to_word_mode @@ -430,6 +460,10 @@ static struct init_keyword { { "volatile", NS_TYPEDEF, .op = &volatile_op }, { "__volatile", NS_TYPEDEF, .op = &volatile_op }, { "__volatile__", NS_TYPEDEF, .op = &volatile_op }, + { "restrict", NS_TYPEDEF, .op = &restrict_op}, + { "__restrict", NS_TYPEDEF, .op = &restrict_op}, + { "__restrict__", NS_TYPEDEF, .op = &restrict_op}, + { "_Atomic", NS_TYPEDEF, .op = &atomic_op}, /* Typedef.. */ { "typedef", NS_TYPEDEF, .op = &typedef_op }, @@ -448,13 +482,17 @@ static struct init_keyword { { "unsigned", NS_TYPEDEF, .op = &unsigned_op }, { "__int128", NS_TYPEDEF, .op = &int128_op }, { "_Bool", NS_TYPEDEF, .type = &bool_ctype, .op = &spec_op }, - { "_Complex", NS_TYPEDEF, .op = &complex_op }, /* Predeclared types */ { "__builtin_va_list", NS_TYPEDEF, .type = &ptr_ctype, .op = &spec_op }, { "__builtin_ms_va_list", NS_TYPEDEF, .type = &ptr_ctype, .op = &spec_op }, { "__int128_t", NS_TYPEDEF, .type = &lllong_ctype, .op = &spec_op }, { "__uint128_t",NS_TYPEDEF, .type = &ulllong_ctype, .op = &spec_op }, + { "_Float32", NS_TYPEDEF, .type = &float32_ctype, .op = &spec_op }, + { "_Float32x", NS_TYPEDEF, .type = &float32x_ctype, .op = &spec_op }, + { "_Float64", NS_TYPEDEF, .type = &float64_ctype, .op = &spec_op }, + { "_Float64x", NS_TYPEDEF, .type = &float64x_ctype, .op = &spec_op }, + { "_Float128", NS_TYPEDEF, .type = &float128_ctype, .op = &spec_op }, /* Extended types */ { "typeof", NS_TYPEDEF, .op = &typeof_op }, @@ -476,11 +514,6 @@ static struct init_keyword { { "_Alignas", NS_TYPEDEF, .op = &alignas_op }, - /* Ignored for now.. */ - { "restrict", NS_TYPEDEF, .op = &restrict_op}, - { "__restrict", NS_TYPEDEF, .op = &restrict_op}, - { "__restrict__", NS_TYPEDEF, .op = &restrict_op}, - /* Static assertion */ { "_Static_assert", NS_KEYWORD, .op = &static_assert_op }, @@ -522,9 +555,10 @@ static struct init_keyword { { "bitwise", NS_KEYWORD, MOD_BITWISE, .op = &attr_bitwise_op }, { "__bitwise__",NS_KEYWORD, MOD_BITWISE, .op = &attr_bitwise_op }, { "address_space",NS_KEYWORD, .op = &address_space_op }, - { "mode", NS_KEYWORD, .op = &mode_op }, { "context", NS_KEYWORD, .op = &context_op }, { "designated_init", NS_KEYWORD, .op = &designated_init_op }, + { "__designated_init__", NS_KEYWORD, .op = &designated_init_op }, + { "transparent_union", NS_KEYWORD, .op = &transparent_union_op }, { "__transparent_union__", NS_KEYWORD, .op = &transparent_union_op }, { "noreturn", NS_KEYWORD, MOD_NORETURN, .op = &attr_mod_op }, { "__noreturn__", NS_KEYWORD, MOD_NORETURN, .op = &attr_mod_op }, @@ -533,20 +567,27 @@ static struct init_keyword { {"const", NS_KEYWORD, MOD_PURE, .op = &attr_mod_op }, {"__const", NS_KEYWORD, MOD_PURE, .op = &attr_mod_op }, {"__const__", NS_KEYWORD, MOD_PURE, .op = &attr_mod_op }, + {"externally_visible", NS_KEYWORD, .op = &ext_visible_op }, + {"__externally_visible__", NS_KEYWORD, .op = &ext_visible_op }, + { "mode", NS_KEYWORD, .op = &mode_op }, { "__mode__", NS_KEYWORD, .op = &mode_op }, - { "QI", NS_KEYWORD, MOD_CHAR, .op = &mode_QI_op }, - { "__QI__", NS_KEYWORD, MOD_CHAR, .op = &mode_QI_op }, - { "HI", NS_KEYWORD, MOD_SHORT, .op = &mode_HI_op }, - { "__HI__", NS_KEYWORD, MOD_SHORT, .op = &mode_HI_op }, - { "SI", NS_KEYWORD, .op = &mode_SI_op }, - { "__SI__", NS_KEYWORD, .op = &mode_SI_op }, - { "DI", NS_KEYWORD, MOD_LONGLONG, .op = &mode_DI_op }, - { "__DI__", NS_KEYWORD, MOD_LONGLONG, .op = &mode_DI_op }, - { "TI", NS_KEYWORD, MOD_LONGLONGLONG, .op = &mode_TI_op }, - { "__TI__", NS_KEYWORD, MOD_LONGLONGLONG, .op = &mode_TI_op }, - { "word", NS_KEYWORD, MOD_LONG, .op = &mode_word_op }, - { "__word__", NS_KEYWORD, MOD_LONG, .op = &mode_word_op }, + { "QI", NS_KEYWORD, .op = &mode_QI_op }, + { "__QI__", NS_KEYWORD, .op = &mode_QI_op }, + { "HI", NS_KEYWORD, .op = &mode_HI_op }, + { "__HI__", NS_KEYWORD, .op = &mode_HI_op }, + { "SI", NS_KEYWORD, .op = &mode_SI_op }, + { "__SI__", NS_KEYWORD, .op = &mode_SI_op }, + { "DI", NS_KEYWORD, .op = &mode_DI_op }, + { "__DI__", NS_KEYWORD, .op = &mode_DI_op }, + { "TI", NS_KEYWORD, .op = &mode_TI_op }, + { "__TI__", NS_KEYWORD, .op = &mode_TI_op }, + { "byte", NS_KEYWORD, .op = &mode_QI_op }, + { "__byte__", NS_KEYWORD, .op = &mode_QI_op }, + { "pointer", NS_KEYWORD, .op = &mode_pointer_op }, + { "__pointer__",NS_KEYWORD, .op = &mode_pointer_op }, + { "word", NS_KEYWORD, .op = &mode_word_op }, + { "__word__", NS_KEYWORD, .op = &mode_word_op }, }; @@ -767,76 +808,108 @@ static struct token *union_specifier(struct token *token, struct decl_state *ctx return struct_union_enum_specifier(SYM_UNION, token, ctx, parse_union_declaration); } - -typedef struct { - int x; - unsigned long long y; -} Num; - -static void upper_boundary(Num *n, Num *v) +/// +// safe right shift +// +// This allow to use a shift amount as big (or bigger) +// than the width of the value to be shifted, in which case +// the result is, of course, 0. +static unsigned long long rshift(unsigned long long val, unsigned int n) { - if (n->x > v->x) - return; - if (n->x < v->x) { - *n = *v; - return; - } - if (n->y < v->y) - n->y = v->y; + if (n >= (sizeof(val) * 8)) + return 0; + return val >> n; } -static void lower_boundary(Num *n, Num *v) +struct range { + long long neg; + unsigned long long pos; +}; + +static void update_range(struct range *range, unsigned long long uval, struct symbol *vtype) { - if (n->x < v->x) - return; - if (n->x > v->x) { - *n = *v; - return; + long long sval = uval; + + if (is_signed_type(vtype) && (sval < 0)) { + if (sval < range->neg) + range->neg = sval; + } else { + if (uval > range->pos) + range->pos = uval; } - if (n->y > v->y) - n->y = v->y; } -static int type_is_ok(struct symbol *type, Num *upper, Num *lower) +static int type_is_ok(struct symbol *type, struct range range) { int shift = type->bit_size; int is_unsigned = type->ctype.modifiers & MOD_UNSIGNED; if (!is_unsigned) shift--; - if (upper->x == 0 && upper->y >> shift) + if (rshift(range.pos, shift)) return 0; - if (lower->x == 0 || (!is_unsigned && (~lower->y >> shift) == 0)) + if (range.neg == 0) return 1; - return 0; + if (is_unsigned) + return 0; + if (rshift(~range.neg, shift)) + return 0; + return 1; } -static struct symbol *bigger_enum_type(struct symbol *s1, struct symbol *s2) +static struct range type_range(struct symbol *type) { - if (s1->bit_size < s2->bit_size) { - s1 = s2; - } else if (s1->bit_size == s2->bit_size) { - if (s2->ctype.modifiers & MOD_UNSIGNED) - s1 = s2; + struct range range; + unsigned int size = type->bit_size; + unsigned long long max; + long long min; + + if (is_signed_type(type)) { + min = sign_bit(size); + max = min - 1; + } else { + min = 0; + max = bits_mask(size); } - if (s1->bit_size < bits_in_int) - return &int_ctype; - return s1; + + range.pos = max; + range.neg = min; + return range; +} + +static int val_in_range(struct range *range, long long sval, struct symbol *vtype) +{ + unsigned long long uval = sval; + + if (is_signed_type(vtype) && (sval < 0)) + return range->neg <= sval; + else + return uval <= range->pos; } static void cast_enum_list(struct symbol_list *list, struct symbol *base_type) { + struct range irange = type_range(&int_ctype); struct symbol *sym; FOR_EACH_PTR(list, sym) { struct expression *expr = sym->initializer; struct symbol *ctype; + long long val; if (expr->type != EXPR_VALUE) continue; ctype = expr->ctype; - if (ctype->bit_size == base_type->bit_size) + val = get_expression_value(expr); + if (is_int_type(ctype) && val_in_range(&irange, val, ctype)) { + expr->ctype = &int_ctype; + continue; + } + if (ctype->bit_size == base_type->bit_size) { + expr->ctype = base_type; continue; + } cast_value(expr, base_type, expr, ctype); + expr->ctype = base_type; } END_FOR_EACH_PTR(sym); } @@ -844,7 +917,8 @@ static struct token *parse_enum_declaration(struct token *token, struct symbol * { unsigned long long lastval = 0; struct symbol *ctype = NULL, *base_type = NULL; - Num upper = {-1, 0}, lower = {1, 0}; + struct range range = { }; + int mix_bitwise = 0; parent->examined = 1; parent->ctype.base_type = &int_ctype; @@ -900,26 +974,29 @@ static struct token *parse_enum_declaration(struct token *token, struct symbol * * base type is at least "int_ctype". * - otherwise the base_type is "bad_ctype". */ - if (!base_type) { + if (!base_type || ctype == &bad_ctype) { base_type = ctype; } else if (ctype == base_type) { /* nothing */ } else if (is_int_type(base_type) && is_int_type(ctype)) { - base_type = bigger_enum_type(base_type, ctype); - } else + base_type = &int_ctype; + } else if (is_restricted_type(base_type) != is_restricted_type(ctype)) { + if (!mix_bitwise++) { + warning(expr->pos, "mixed bitwiseness"); + } + } else if (is_restricted_type(base_type) && base_type != ctype) { + sparse_error(expr->pos, "incompatible restricted type"); + info(expr->pos, " expected: %s", show_typename(base_type)); + info(expr->pos, " got: %s", show_typename(ctype)); base_type = &bad_ctype; + } else if (base_type != &bad_ctype) { + sparse_error(token->pos, "bad enum definition"); + base_type = &bad_ctype; + } parent->ctype.base_type = base_type; } if (is_int_type(base_type)) { - Num v = {.y = lastval}; - if (ctype->ctype.modifiers & MOD_UNSIGNED) - v.x = 0; - else if ((long long)lastval >= 0) - v.x = 0; - else - v.x = -1; - upper_boundary(&upper, &v); - lower_boundary(&lower, &v); + update_range(&range, lastval, ctype); } token = next; @@ -930,31 +1007,31 @@ static struct token *parse_enum_declaration(struct token *token, struct symbol * token = token->next; } if (!base_type) { - sparse_error(token->pos, "bad enum definition"); + sparse_error(token->pos, "empty enum definition"); base_type = &bad_ctype; } else if (!is_int_type(base_type)) - base_type = base_type; - else if (type_is_ok(base_type, &upper, &lower)) - base_type = base_type; - else if (type_is_ok(&int_ctype, &upper, &lower)) - base_type = &int_ctype; - else if (type_is_ok(&uint_ctype, &upper, &lower)) + ; + else if (type_is_ok(&uint_ctype, range)) base_type = &uint_ctype; - else if (type_is_ok(&long_ctype, &upper, &lower)) - base_type = &long_ctype; - else if (type_is_ok(&ulong_ctype, &upper, &lower)) + else if (type_is_ok(&int_ctype, range)) + base_type = &int_ctype; + else if (type_is_ok(&ulong_ctype, range)) base_type = &ulong_ctype; - else if (type_is_ok(&llong_ctype, &upper, &lower)) - base_type = &llong_ctype; - else if (type_is_ok(&ullong_ctype, &upper, &lower)) + else if (type_is_ok(&long_ctype, range)) + base_type = &long_ctype; + else if (type_is_ok(&ullong_ctype, range)) base_type = &ullong_ctype; + else if (type_is_ok(&llong_ctype, range)) + base_type = &llong_ctype; else base_type = &bad_ctype; parent->ctype.base_type = base_type; parent->ctype.modifiers |= (base_type->ctype.modifiers & MOD_UNSIGNED); parent->examined = 0; + if (mix_bitwise) + return token; cast_enum_list(parent->symbol_list, base_type); return token; @@ -1043,6 +1120,12 @@ static struct token *attribute_modifier(struct token *token, struct symbol *attr return token; } +static struct token *attribute_ext_visible(struct token *token, struct symbol *attr, struct decl_state *ctx) +{ + ctx->is_ext_visible = 1; + return token; +} + static struct token *attribute_bitwise(struct token *token, struct symbol *attr, struct decl_state *ctx) { if (Wbitwise) @@ -1050,18 +1133,49 @@ static struct token *attribute_bitwise(struct token *token, struct symbol *attr, return token; } +static struct ident *numerical_address_space(int asn) +{ + char buff[32]; + + if (!asn) + return NULL; + sprintf(buff, "<asn:%d>", asn); + return built_in_ident(buff); +} + static struct token *attribute_address_space(struct token *token, struct symbol *attr, struct decl_state *ctx) { struct expression *expr = NULL; - int as; + struct ident *as = NULL; + struct token *next; + token = expect(token, '(', "after address_space attribute"); - token = conditional_expression(token, &expr); - if (expr) { - as = const_expression_value(expr); - if (Waddress_space && as) - ctx->ctype.as = as; + switch (token_type(token)) { + case TOKEN_NUMBER: + next = primary_expression(token, &expr); + if (expr->type != EXPR_VALUE) + goto invalid; + as = numerical_address_space(expr->value); + break; + case TOKEN_IDENT: + next = token->next; + as = token->ident; + break; + default: + next = token->next; + invalid: + as = NULL; + warning(token->pos, "invalid address space name"); + } + + if (Waddress_space && as) { + if (ctx->ctype.as) + sparse_error(token->pos, + "multiple address space given: %s & %s", + show_as(ctx->ctype.as), show_as(as)); + ctx->ctype.as = as; } - token = expect(token, ')', "after address_space attribute"); + token = expect(next, ')', "after address_space attribute"); return token; } @@ -1107,6 +1221,14 @@ static struct symbol *to_TI_mode(struct symbol *ctype) : &slllong_ctype; } +static struct symbol *to_pointer_mode(struct symbol *ctype) +{ + if (ctype->ctype.base_type != &int_type) + return NULL; + return ctype->ctype.modifiers & MOD_UNSIGNED ? uintptr_ctype + : intptr_ctype; +} + static struct symbol *to_word_mode(struct symbol *ctype) { if (ctype->ctype.base_type != &int_type) @@ -1123,7 +1245,7 @@ static struct token *attribute_mode(struct token *token, struct symbol *attr, st if (mode && mode->op->type == KW_MODE) ctx->mode = mode->op; else - sparse_error(token->pos, "unknown mode attribute %s\n", show_ident(token->ident)); + sparse_error(token->pos, "unknown mode attribute %s", show_ident(token->ident)); token = token->next; } else sparse_error(token->pos, "expect attribute mode symbol\n"); @@ -1135,43 +1257,24 @@ static struct token *attribute_context(struct token *token, struct symbol *attr, { struct context *context = alloc_context(); struct expression *args[3]; - int argc = 0; + int idx = 0; token = expect(token, '(', "after context attribute"); - while (!match_op(token, ')')) { - struct expression *expr = NULL; - token = conditional_expression(token, &expr); - if (!expr) - break; - if (argc < 3) - args[argc++] = expr; - if (!match_op(token, ',')) - break; + token = conditional_expression(token, &args[0]); + token = expect(token, ',', "after context 1st argument"); + token = conditional_expression(token, &args[1]); + if (match_op(token, ',')) { token = token->next; - } - - switch(argc) { - case 0: - sparse_error(token->pos, "expected context input/output values"); - break; - case 1: - context->in = get_expression_value(args[0]); - break; - case 2: - context->in = get_expression_value(args[0]); - context->out = get_expression_value(args[1]); - break; - case 3: + token = conditional_expression(token, &args[2]); + token = expect(token, ')', "after context 3rd argument"); context->context = args[0]; - context->in = get_expression_value(args[1]); - context->out = get_expression_value(args[2]); - break; + idx++; + } else { + token = expect(token, ')', "after context 2nd argument"); } - - if (argc) - add_ptr_list(&ctx->ctype.contexts, context); - - token = expect(token, ')', "after context attribute"); + context->in = get_expression_value(args[idx++]); + context->out = get_expression_value(args[idx++]); + add_ptr_list(&ctx->ctype.contexts, context); return token; } @@ -1201,7 +1304,7 @@ static struct token *recover_unknown_attribute(struct token *token) struct expression *expr = NULL; if (Wunknown_attribute) - warning(token->pos, "attribute '%s': unknown attribute", show_ident(token->ident)); + warning(token->pos, "unknown attribute '%s'", show_ident(token->ident)); token = token->next; if (match_op(token, '(')) token = parens_expression(token, &expr, "in attribute"); @@ -1260,7 +1363,8 @@ static unsigned long storage_modifiers(struct decl_state *ctx) [SRegister] = MOD_REGISTER }; return mod[ctx->storage_class] | (ctx->is_inline ? MOD_INLINE : 0) - | (ctx->is_tls ? MOD_TLS : 0); + | (ctx->is_tls ? MOD_TLS : 0) + | (ctx->is_ext_visible ? MOD_EXT_VISIBLE : 0); } static void set_storage_class(struct position *pos, struct decl_state *ctx, int class) @@ -1391,6 +1495,18 @@ static struct token *volatile_qualifier(struct token *next, struct decl_state *c return next; } +static struct token *restrict_qualifier(struct token *next, struct decl_state *ctx) +{ + apply_qualifier(&next->pos, &ctx->ctype, MOD_RESTRICT); + return next; +} + +static struct token *atomic_qualifier(struct token *next, struct decl_state *ctx) +{ + apply_qualifier(&next->pos, &ctx->ctype, MOD_ATOMIC); + return next; +} + static void apply_ctype(struct position pos, struct ctype *thistype, struct ctype *ctype) { unsigned long mod = thistype->modifiers; @@ -1705,7 +1821,6 @@ static enum kind which_func(struct token *token, if (next->special == ')') { /* don't complain about those */ - if (!n || match_op(next->next, ';')) if (!n || match_op(next->next, ';') || match_op(next->next, ',')) return Empty; if (Wstrict_prototypes) @@ -1781,7 +1896,7 @@ static struct token *pointer(struct token *token, struct decl_state *ctx) ptr->ctype.contexts = ctx->ctype.contexts; ctx->ctype.modifiers = 0; ctx->ctype.base_type = ptr; - ctx->ctype.as = 0; + ctx->ctype.as = NULL; ctx->ctype.contexts = NULL; ctx->ctype.alignment = 0; @@ -1964,24 +2079,20 @@ static struct token *expression_statement(struct token *token, struct expression static struct token *parse_asm_operands(struct token *token, struct statement *stmt, struct expression_list **inout) { - struct expression *expr; - /* Allow empty operands */ if (match_op(token->next, ':') || match_op(token->next, ')')) return token->next; do { - struct ident *ident = NULL; + struct expression *op = alloc_expression(token->pos, EXPR_ASM_OPERAND); if (match_op(token->next, '[') && token_type(token->next->next) == TOKEN_IDENT && match_op(token->next->next->next, ']')) { - ident = token->next->next->ident; + op->name = token->next->next->ident; token = token->next->next->next; } - add_expression(inout, (struct expression *)ident); /* UGGLEE!!! */ - token = primary_expression(token->next, &expr); - add_expression(inout, expr); - token = parens_expression(token, &expr, "in asm parameter"); - add_expression(inout, expr); + token = primary_expression(token->next, &op->constraint); + token = parens_expression(token, &op->expr, "in asm parameter"); + add_expression(inout, op); } while (match_op(token, ',')); return token; } @@ -2017,15 +2128,16 @@ static struct token *parse_asm_labels(struct token *token, struct statement *stm static struct token *parse_asm_statement(struct token *token, struct statement *stmt) { - int is_goto = 0; + unsigned long mods = 0; token = token->next; stmt->type = STMT_ASM; - if (match_idents(token, &__volatile___ident, &__volatile_ident, &volatile_ident, NULL)) { - token = token->next; - } - if (token_type(token) == TOKEN_IDENT && token->ident == &goto_ident) { - is_goto = 1; + while (token_type(token) == TOKEN_IDENT) { + struct symbol *s = lookup_keyword(token->ident, NS_TYPEDEF); + if (s && s->op && s->op->asm_modifier) + s->op->asm_modifier(token, &mods); + else if (token->ident == &goto_ident) + asm_modifier(token, &mods, MOD_ASM_GOTO); token = token->next; } token = expect(token, '(', "after asm"); @@ -2036,7 +2148,7 @@ static struct token *parse_asm_statement(struct token *token, struct statement * token = parse_asm_operands(token, stmt, &stmt->asm_inputs); if (match_op(token, ':')) token = parse_asm_clobbers(token, stmt, &stmt->asm_clobbers); - if (is_goto && match_op(token, ':')) + if (match_op(token, ':') && (mods & MOD_ASM_GOTO)) token = parse_asm_labels(token, stmt, &stmt->asm_labels); token = expect(token, ')', "after asm"); return expect(token, ';', "at end of asm-statement"); @@ -2129,7 +2241,7 @@ static struct statement *start_function(struct symbol *sym) start_function_scope(sym->pos); ret = alloc_symbol(sym->pos, SYM_NODE); ret->ctype = sym->ctype.base_type->ctype; - ret->ctype.modifiers &= ~(MOD_STORAGE | MOD_CONST | MOD_VOLATILE | MOD_TLS | MOD_INLINE | MOD_ADDRESSABLE | MOD_NOCAST | MOD_NODEREF | MOD_ACCESSED | MOD_TOPLEVEL); + ret->ctype.modifiers &= ~(MOD_STORAGE | MOD_QUALIFIER | MOD_TLS | MOD_ACCESS | MOD_NOCAST | MOD_NODEREF); ret->ctype.modifiers |= (MOD_AUTO | MOD_REGISTER); bind_symbol(ret, &return_ident, NS_ITERATOR); stmt->ret = ret; @@ -2372,26 +2484,33 @@ static struct token *parse_goto_statement(struct token *token, struct statement static struct token *parse_context_statement(struct token *token, struct statement *stmt) { stmt->type = STMT_CONTEXT; - token = parse_expression(token->next, &stmt->expression); - if (stmt->expression->type == EXPR_PREOP - && stmt->expression->op == '(' - && stmt->expression->unop->type == EXPR_COMMA) { - struct expression *expr; - expr = stmt->expression->unop; - stmt->context = expr->left; - stmt->expression = expr->right; + token = token->next; + token = expect(token, '(', "after __context__ statement"); + token = assignment_expression(token, &stmt->expression); + if (!stmt->expression) + unexpected(token, "expression expected after '('"); + if (match_op(token, ',')) { + token = token->next; + stmt->context = stmt->expression; + token = assignment_expression(token, &stmt->expression); + if (!stmt->expression) + unexpected(token, "expression expected after ','"); } + token = expect(token, ')', "at end of __context__ statement"); return expect(token, ';', "at end of statement"); } static struct token *parse_range_statement(struct token *token, struct statement *stmt) { stmt->type = STMT_RANGE; - token = assignment_expression(token->next, &stmt->range_expression); + token = token->next; + token = expect(token, '(', "after __range__ statement"); + token = assignment_expression(token, &stmt->range_expression); token = expect(token, ',', "after range expression"); token = assignment_expression(token, &stmt->range_low); token = expect(token, ',', "after low range"); token = assignment_expression(token, &stmt->range_high); + token = expect(token, ')', "after range statement"); return expect(token, ';', "after range statement"); } @@ -2407,12 +2526,15 @@ static struct token *statement(struct token *token, struct statement **tree) if (match_op(token->next, ':')) { struct symbol *s = label_symbol(token); + token = skip_attributes(token->next->next); + if (s->stmt) { + sparse_error(stmt->pos, "label '%s' redefined", show_ident(s->ident)); + // skip the label to avoid multiple definitions + return statement(token, tree); + } stmt->type = STMT_LABEL; stmt->label_identifier = s; - if (s->stmt) - sparse_error(stmt->pos, "label '%s' redefined", show_ident(token->ident)); s->stmt = stmt; - token = skip_attributes(token->next->next); return statement(token, &stmt->label_statement); } } @@ -2697,11 +2819,10 @@ static struct token *parse_function_body(struct token *token, struct symbol *dec function_computed_target_list = NULL; function_computed_goto_list = NULL; - if (decl->ctype.modifiers & MOD_EXTERN && - !(decl->ctype.modifiers & MOD_INLINE) && - Wexternal_function_has_definition) - warning(decl->pos, "function '%s' with external linkage has definition", show_ident(decl->ident)); - + if ((decl->ctype.modifiers & (MOD_EXTERN|MOD_INLINE)) == MOD_EXTERN) { + if (Wexternal_function_has_definition) + warning(decl->pos, "function '%s' with external linkage has definition", show_ident(decl->ident)); + } if (!(decl->ctype.modifiers & MOD_STATIC)) decl->ctype.modifiers |= MOD_EXTERN; @@ -2777,7 +2898,9 @@ static void apply_k_r_types(struct symbol_list *argtypes, struct symbol *fn) sparse_error(arg->pos, "missing type declaration for parameter '%s'", show_ident(arg->ident)); } - continue; + type = alloc_symbol(arg->pos, SYM_NODE); + type->ident = arg->ident; + type->ctype.base_type = &int_ctype; match: type->used = 1; /* "char" and "short" promote to "int" */ @@ -2950,6 +3073,10 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis if (decl->same_symbol) { decl->definition = decl->same_symbol->definition; decl->op = decl->same_symbol->op; + if (is_typedef) { + // TODO: handle -std=c89 --pedantic + check_duplicates(decl); + } } if (!match_op(token, ',')) diff --git a/usr/src/tools/smatch/src/parse.h b/usr/src/tools/smatch/src/parse.h index f3612f5258..8773cf0572 100644 --- a/usr/src/tools/smatch/src/parse.h +++ b/usr/src/tools/smatch/src/parse.h @@ -135,7 +135,6 @@ extern struct token *external_declaration(struct token *, struct symbol_list **, extern struct symbol *ctype_integer(int size, int want_unsigned); -extern void copy_statement(struct statement *src, struct statement *dst); extern int inline_function(struct expression *expr, struct symbol *sym); extern void uninline(struct symbol *sym); extern void init_parser(int); diff --git a/usr/src/tools/smatch/src/pre-process.c b/usr/src/tools/smatch/src/pre-process.c index a33ef69222..c35cbcfb3c 100644 --- a/usr/src/tools/smatch/src/pre-process.c +++ b/usr/src/tools/smatch/src/pre-process.c @@ -49,6 +49,7 @@ static struct ident_list *macros; // only needed for -dD static int false_nesting = 0; static int counter_macro = 0; // __COUNTER__ expansion +static int include_level = 0; #define INCLUDEPATHS 300 const char *includepath[INCLUDEPATHS+1] = { @@ -147,49 +148,96 @@ static int token_defined(struct token *token) return 0; } -static void replace_with_defined(struct token *token) +static void replace_with_bool(struct token *token, bool val) { static const char *string[] = { "0", "1" }; - int defined = token_defined(token); token_type(token) = TOKEN_NUMBER; - token->number = string[defined]; + token->number = string[val]; +} + +static void replace_with_defined(struct token *token) +{ + replace_with_bool(token, token_defined(token)); +} + +static void replace_with_has_builtin(struct token *token) +{ + struct symbol *sym = lookup_symbol(token->ident, NS_SYMBOL); + replace_with_bool(token, sym && sym->builtin); +} + +static void replace_with_has_attribute(struct token *token) +{ + struct symbol *sym = lookup_symbol(token->ident, NS_KEYWORD); + replace_with_bool(token, sym && sym->op && sym->op->attribute); +} + +static void expand_line(struct token *token) +{ + replace_with_integer(token, token->pos.line); +} + +static void expand_file(struct token *token) +{ + replace_with_string(token, stream_name(token->pos.stream)); +} + +static void expand_basefile(struct token *token) +{ + replace_with_string(token, base_filename); +} + +static time_t t = 0; +static void expand_date(struct token *token) +{ + static char buffer[12]; /* __DATE__: 3 + ' ' + 2 + ' ' + 4 + '\0' */ + + if (!t) + time(&t); + strftime(buffer, 12, "%b %e %Y", localtime(&t)); + replace_with_string(token, buffer); +} + +static void expand_time(struct token *token) +{ + static char buffer[9]; /* __TIME__: 2 + ':' + 2 + ':' + 2 + '\0' */ + + if (!t) + time(&t); + strftime(buffer, 9, "%T", localtime(&t)); + replace_with_string(token, buffer); +} + +static void expand_counter(struct token *token) +{ + replace_with_integer(token, counter_macro++); +} + +static void expand_include_level(struct token *token) +{ + replace_with_integer(token, include_level - 1); } static int expand_one_symbol(struct token **list) { struct token *token = *list; struct symbol *sym; - static char buffer[12]; /* __DATE__: 3 + ' ' + 2 + ' ' + 4 + '\0' */ - static time_t t = 0; if (token->pos.noexpand) return 1; sym = lookup_macro(token->ident); - if (sym) { - store_macro_pos(token); + if (!sym) + return 1; + store_macro_pos(token); + if (sym->expander) { + sym->expander(token); + return 1; + } else { sym->used_in = file_scope; return expand(list, sym); } - if (token->ident == &__LINE___ident) { - replace_with_integer(token, token->pos.line); - } else if (token->ident == &__FILE___ident) { - replace_with_string(token, stream_name(token->pos.stream)); - } else if (token->ident == &__DATE___ident) { - if (!t) - time(&t); - strftime(buffer, 12, "%b %e %Y", localtime(&t)); - replace_with_string(token, buffer); - } else if (token->ident == &__TIME___ident) { - if (!t) - time(&t); - strftime(buffer, 9, "%T", localtime(&t)); - replace_with_string(token, buffer); - } else if (token->ident == &__COUNTER___ident) { - replace_with_integer(token, counter_macro++); - } - return 1; } static inline struct token *scan_next(struct token **where) @@ -514,13 +562,10 @@ static int merge(struct token *left, struct token *right) left->pos.noexpand = 0; return 1; - case TOKEN_NUMBER: { - char *number = __alloc_bytes(strlen(buffer) + 1); - memcpy(number, buffer, strlen(buffer) + 1); + case TOKEN_NUMBER: token_type(left) = TOKEN_NUMBER; /* could be . + num */ - left->number = number; + left->number = xstrdup(buffer); return 1; - } case TOKEN_SPECIAL: if (buffer[2] && buffer[3]) @@ -851,6 +896,10 @@ static void set_stream_include_path(struct stream *stream) includepath[0] = path; } +#ifndef PATH_MAX +#define PATH_MAX 4096 // for Hurd where it's not defined +#endif + static int try_include(const char *path, const char *filename, int flen, struct token **where, const char **next_path) { int fd; @@ -867,8 +916,7 @@ static int try_include(const char *path, const char *filename, int flen, struct return 1; fd = open(fullname, O_RDONLY); if (fd >= 0) { - char * streamname = __alloc_bytes(plen + flen); - memcpy(streamname, fullname, plen + flen); + char *streamname = xmemdup(fullname, plen + flen); *where = tokenize(streamname, fd, *where, next_path); close(fd); return 1; @@ -912,13 +960,14 @@ const char *find_include(const char *skip, const char *look_for) return NULL; if (!getcwd(cwd, sizeof(cwd))) - return NULL; + goto close; while ((entry = readdir(dp))) { lstat(entry->d_name, &statbuf); if (strcmp(entry->d_name, look_for) == 0) { snprintf(buf, sizeof(buf), "%s/%s", cwd, entry->d_name); + closedir(dp); return buf; } @@ -932,10 +981,13 @@ const char *find_include(const char *skip, const char *look_for) chdir(entry->d_name); ret = find_include("", look_for); chdir(".."); - if (ret) + if (ret) { + closedir(dp); return ret; + } } } +close: closedir(dp); return NULL; @@ -982,8 +1034,13 @@ static void use_best_guess_header_file(struct token *token, const char *filename char dir_part[PATH_MAX]; const char *file_part; const char *include_name; + static int cnt; int len; + /* Avoid guessing includes recursively. */ + if (cnt++ > 1000) + return; + if (!filename || filename[0] == '\0') return; @@ -1420,40 +1477,16 @@ Earg: return NULL; } -static int do_handle_define(struct stream *stream, struct token **line, struct token *token, int attr) +static int do_define(struct position pos, struct token *token, struct ident *name, + struct token *arglist, struct token *expansion, int attr) { - struct token *arglist, *expansion; - struct token *left = token->next; struct symbol *sym; - struct ident *name; - int ret; - - if (token_type(left) != TOKEN_IDENT) { - sparse_error(token->pos, "expected identifier to 'define'"); - return 1; - } - - name = left->ident; - - arglist = NULL; - expansion = left->next; - if (!expansion->pos.whitespace) { - if (match_op(expansion, '(')) { - arglist = expansion; - expansion = parse_arguments(expansion); - if (!expansion) - return 1; - } else if (!eof_token(expansion)) { - warning(expansion->pos, - "no whitespace before object-like macro body"); - } - } + int ret = 1; expansion = parse_expansion(expansion, arglist, name); if (!expansion) return 1; - ret = 1; sym = lookup_symbol(name, NS_MACRO | NS_UNDEF); if (sym) { int clean; @@ -1468,7 +1501,7 @@ static int do_handle_define(struct stream *stream, struct token **line, struct t ret = 0; if ((clean && attr == SYM_ATTR_NORMAL) || sym->used_in == file_scope) { - warning(left->pos, "preprocessor token %.*s redefined", + warning(pos, "preprocessor token %.*s redefined", name->len, name->name); info(sym->pos, "this was the original definition"); } @@ -1477,7 +1510,7 @@ static int do_handle_define(struct stream *stream, struct token **line, struct t } if (!sym || sym->scope != file_scope) { - sym = alloc_symbol(left->pos, SYM_NODE); + sym = alloc_symbol(pos, SYM_NODE); bind_symbol(sym, name, NS_MACRO); add_ident(¯os, name); ret = 0; @@ -1486,7 +1519,8 @@ static int do_handle_define(struct stream *stream, struct token **line, struct t if (!ret) { sym->expansion = expansion; sym->arglist = arglist; - __free_token(token); /* Free the "define" token, but not the rest of the line */ + if (token) /* Free the "define" token, but not the rest of the line */ + __free_token(token); } sym->namespace = NS_MACRO; @@ -1496,6 +1530,74 @@ out: return ret; } +/// +// predefine a macro with a printf-formatted value +// @name: the name of the macro +// @weak: 0/1 for a normal or a weak define +// @fmt: the printf format followed by it's arguments. +// +// The type of the value is automatically infered: +// TOKEN_NUMBER if it starts by a digit, TOKEN_IDENT otherwise. +// If @fmt is null or empty, the macro is defined with an empty definition. +void predefine(const char *name, int weak, const char *fmt, ...) +{ + struct ident *ident = built_in_ident(name); + struct token *value = &eof_token_entry; + int attr = weak ? SYM_ATTR_WEAK : SYM_ATTR_NORMAL; + + if (fmt && fmt[0]) { + static char buf[256]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + value = __alloc_token(0); + if (isdigit(buf[0])) { + token_type(value) = TOKEN_NUMBER; + value->number = xstrdup(buf); + } else { + token_type(value) = TOKEN_IDENT; + value->ident = built_in_ident(buf); + } + value->pos.whitespace = 1; + value->next = &eof_token_entry; + } + + do_define(value->pos, NULL, ident, NULL, value, attr); +} + +static int do_handle_define(struct stream *stream, struct token **line, struct token *token, int attr) +{ + struct token *arglist, *expansion; + struct token *left = token->next; + struct ident *name; + + if (token_type(left) != TOKEN_IDENT) { + sparse_error(token->pos, "expected identifier to 'define'"); + return 1; + } + + name = left->ident; + + arglist = NULL; + expansion = left->next; + if (!expansion->pos.whitespace) { + if (match_op(expansion, '(')) { + arglist = expansion; + expansion = parse_arguments(expansion); + if (!expansion) + return 1; + } else if (!eof_token(expansion)) { + warning(expansion->pos, + "no whitespace before object-like macro body"); + } + } + + return do_define(left->pos, token, name, arglist, expansion, attr); +} + static int handle_define(struct stream *stream, struct token **line, struct token *token) { return do_handle_define(stream, line, token, SYM_ATTR_NORMAL); @@ -1552,13 +1654,13 @@ static int handle_strong_undef(struct stream *stream, struct token **line, struc return do_handle_undef(stream, line, token, SYM_ATTR_STRONG); } -static int preprocessor_if(struct stream *stream, struct token *token, int true) +static int preprocessor_if(struct stream *stream, struct token *token, int cond) { token_type(token) = false_nesting ? TOKEN_SKIP_GROUPS : TOKEN_IF; free_preprocessor_line(token->next); token->next = stream->top_if; stream->top_if = token; - if (false_nesting || true != 1) + if (false_nesting || cond != 1) false_nesting++; return 0; } @@ -1626,6 +1728,14 @@ static int expression_value(struct token **where) state = 1; beginning = list; break; + } else if (p->ident == &__has_builtin_ident) { + state = 4; + beginning = list; + break; + } else if (p->ident == &__has_attribute_ident) { + state = 6; + beginning = list; + break; } if (!expand_one_symbol(list)) continue; @@ -1656,6 +1766,38 @@ static int expression_value(struct token **where) sparse_error(p->pos, "missing ')' after \"defined\""); *list = p->next; continue; + + // __has_builtin(x) or __has_attribute(x) + case 4: case 6: + if (match_op(p, '(')) { + state++; + } else { + sparse_error(p->pos, "missing '(' after \"__has_%s\"", + state == 4 ? "builtin" : "attribute"); + state = 0; + } + *beginning = p; + break; + case 5: case 7: + if (token_type(p) != TOKEN_IDENT) { + sparse_error(p->pos, "identifier expected"); + state = 0; + break; + } + if (!match_op(p->next, ')')) + sparse_error(p->pos, "missing ')' after \"__has_%s\"", + state == 5 ? "builtin" : "attribute"); + if (state == 5) + replace_with_has_builtin(p); + else + replace_with_has_attribute(p); + state = 8; + *beginning = p; + break; + case 8: + state = 0; + *list = p->next; + continue; } list = &p->next; } @@ -1969,9 +2111,6 @@ static int handle_line(struct stream *stream, struct token **line, struct token return 1; } -/* - * Ignore "#ident". - */ static int handle_ident(struct stream *stream, struct token **line, struct token *token) { return 1; @@ -2021,6 +2160,18 @@ static void init_preprocessor(void) { "if", handle_if }, { "elif", handle_elif }, }; + static struct { + const char *name; + void (*expander)(struct token *); + } dynamic[] = { + { "__LINE__", expand_line }, + { "__FILE__", expand_file }, + { "__BASE_FILE__", expand_basefile }, + { "__DATE__", expand_date }, + { "__TIME__", expand_time }, + { "__COUNTER__", expand_counter }, + { "__INCLUDE_LEVEL__", expand_include_level }, + }; for (i = 0; i < ARRAY_SIZE(normal); i++) { struct symbol *sym; @@ -2034,6 +2185,11 @@ static void init_preprocessor(void) sym->handler = special[i].handler; sym->normal = 0; } + for (i = 0; i < ARRAY_SIZE(dynamic); i++) { + struct symbol *sym; + sym = create_symbol(stream, dynamic[i].name, SYM_NODE, NS_MACRO); + sym->expander = dynamic[i].expander; + } counter_macro = 0; } @@ -2115,9 +2271,11 @@ static void do_preprocess(struct token **list) if (!stream->dirty) stream->constant = CONSTANT_FILE_YES; *list = next->next; + include_level--; continue; case TOKEN_STREAMBEGIN: *list = next->next; + include_level++; continue; default: @@ -2179,6 +2337,12 @@ struct token * preprocess(struct token *token) return token; } +static int is_VA_ARGS_token(struct token *token) +{ + return (token_type(token) == TOKEN_IDENT) && + (token->ident == &__VA_ARGS___ident); +} + static void dump_macro(struct symbol *sym) { int nargs = sym->arglist ? sym->arglist->count.normal : 0; @@ -2194,27 +2358,34 @@ static void dump_macro(struct symbol *sym) for (; !eof_token(token); token = token->next) { if (token_type(token) == TOKEN_ARG_COUNT) continue; - printf("%s%s", sep, show_token(token)); + if (is_VA_ARGS_token(token)) + printf("%s...", sep); + else + printf("%s%s", sep, show_token(token)); args[narg++] = token; - sep = ", "; + sep = ","; } putchar(')'); } - putchar(' '); token = sym->expansion; - while (!eof_token(token)) { + while (token_type(token) != TOKEN_UNTAINT) { struct token *next = token->next; + if (token->pos.whitespace) + putchar(' '); switch (token_type(token)) { - case TOKEN_UNTAINT: + case TOKEN_CONCAT: + printf("##"); break; + case TOKEN_STR_ARGUMENT: + printf("#"); + /* fall-through */ + case TOKEN_QUOTED_ARGUMENT: case TOKEN_MACRO_ARGUMENT: token = args[token->argnum]; /* fall-through */ default: printf("%s", show_token(token)); - if (next->pos.whitespace) - putchar(' '); } token = next; } diff --git a/usr/src/tools/smatch/src/ptrlist.c b/usr/src/tools/smatch/src/ptrlist.c index 635b6bbe59..0c2d7ed53f 100644 --- a/usr/src/tools/smatch/src/ptrlist.c +++ b/usr/src/tools/smatch/src/ptrlist.c @@ -1,10 +1,13 @@ /* * ptrlist.c * - * Pointer list manipulation - * * (C) Copyright Linus Torvalds 2003-2005 */ + +/// +// Pointer list manipulation +// ------------------------- + #include <stdlib.h> #include <string.h> #include <assert.h> @@ -17,6 +20,10 @@ __DECLARE_ALLOCATOR(struct ptr_list, ptrlist); __ALLOCATOR(struct ptr_list, "ptr list", ptrlist); __ALLOCATOR(struct ptr_list, "rl ptr list", rl_ptrlist); +/// +// get the size of a ptrlist +// @head: the head of the list +// @return: the size of the list given by @head. int ptr_list_size(struct ptr_list *head) { int nr = 0; @@ -30,14 +37,120 @@ int ptr_list_size(struct ptr_list *head) return nr; } -/* - * Linearize the entries of a list up to a total of 'max', - * and return the nr of entries linearized. - * - * The array to linearize into (second argument) should really - * be "void *x[]", but we want to let people fill in any kind - * of pointer array, so let's just call it "void **". - */ +/// +// test if a list is empty +// @head: the head of the list +// @return: ``true`` if the list is empty, ``false`` otherwise. +bool ptr_list_empty(const struct ptr_list *head) +{ + const struct ptr_list *list = head; + + if (!head) + return true; + + do { + if (list->nr - list->rm) + return false; + } while ((list = list->next) != head); + + return true; +} + +/// +// test is a list contains more than one element +// @head: the head of the list +// @return: ``true`` if the list has more than 1 element, ``false`` otherwise. +bool ptr_list_multiple(const struct ptr_list *head) +{ + const struct ptr_list *list = head; + int nr = 0; + + if (!head) + return false; + + do { + nr += list->nr - list->rm; + if (nr > 1) + return true; + } while ((list = list->next) != head); + + return false; +} + +/// +// get the first element of a ptrlist +// @head: the head of the list +// @return: the first element of the list or ``NULL`` if the list is empty +void *first_ptr_list(struct ptr_list *head) +{ + struct ptr_list *list = head; + + if (!head) + return NULL; + + while (list->nr == 0) { + list = list->next; + if (list == head) + return NULL; + } + return PTR_ENTRY_NOTAG(list, 0); +} + +/// +// get the last element of a ptrlist +// @head: the head of the list +// @return: the last element of the list or ``NULL`` if the list is empty +void *last_ptr_list(struct ptr_list *head) +{ + struct ptr_list *list; + + if (!head) + return NULL; + list = head->prev; + while (list->nr == 0) { + if (list == head) + return NULL; + list = list->prev; + } + return PTR_ENTRY_NOTAG(list, list->nr-1); +} + +/// +// get the nth element of a ptrlist +// @head: the head of the list +// @return: the nth element of the list or ``NULL`` if the list is too short. +void *ptr_list_nth_entry(struct ptr_list *list, unsigned int idx) +{ + struct ptr_list *head = list; + + if (!head) + return NULL; + + do { + unsigned int nr = list->nr; + + if (idx < nr) + return list->list[idx]; + else + idx -= nr; + } while ((list = list->next) != head); + return NULL; +} + +/// +// linearize the entries of a list +// +// @head: the list to be linearized +// @arr: a ``void*`` array to fill with @head's entries +// @max: the maximum number of entries to store into @arr +// @return: the number of entries linearized. +// +// Linearize the entries of a list up to a total of @max, +// and return the nr of entries linearized. +// +// The array to linearize into (@arr) should really +// be ``void *x[]``, but we want to let people fill in any kind +// of pointer array, so let's just call it ``void **``. int linearize_ptr_list(struct ptr_list *head, void **arr, int max) { int nr = 0; @@ -59,12 +172,15 @@ int linearize_ptr_list(struct ptr_list *head, void **arr, int max) return nr; } -/* - * When we've walked the list and deleted entries, - * we may need to re-pack it so that we don't have - * any empty blocks left (empty blocks upset the - * walking code - */ +/// +// pack a ptrlist +// +// @listp: a pointer to the list to be packed. +// +// When we've walked the list and deleted entries, +// we may need to re-pack it so that we don't have +// any empty blocks left (empty blocks upset the +// walking code). void pack_ptr_list(struct ptr_list **listp) { struct ptr_list *head = *listp; @@ -98,6 +214,13 @@ restart: } } +/// +// split a ptrlist block +// @head: the ptrlist block to be splitted +// +// A new block is inserted just after @head and the entries +// at the half end of @head are moved to this new block. +// The goal being to create space inside @head for a new entry. void split_ptr_list_head(struct ptr_list *head) { int old = head->nr, nr = old / 2; @@ -116,18 +239,21 @@ void split_ptr_list_head(struct ptr_list *head) } int rl_ptrlist_hack; -void **__add_ptr_list(struct ptr_list **listp, void *ptr, unsigned long tag) +/// +// add an entry to a ptrlist +// @listp: a pointer to the list +// @ptr: the entry to add to the list +// @return: the address where the new entry is stored. +// +// :note: code must not use this function and should use +// :func:`add_ptr_list` instead. +void **__add_ptr_list(struct ptr_list **listp, void *ptr) { struct ptr_list *list = *listp; struct ptr_list *last = NULL; /* gcc complains needlessly */ void **ret; int nr; - /* The low two bits are reserved for tags */ - assert((3 & (unsigned long)ptr) == 0); - assert((~3 & tag) == 0); - ptr = (void *)(tag | (unsigned long)ptr); - if (!list || (nr = (last = list->prev)->nr) >= LIST_NODE_NR) { struct ptr_list *newlist; @@ -155,6 +281,52 @@ void **__add_ptr_list(struct ptr_list **listp, void *ptr, unsigned long tag) return ret; } +/// +// add a tagged entry to a ptrlist +// @listp: a pointer to the list +// @ptr: the entry to add to the list +// @tag: the tag to add to @ptr +// @return: the address where the new entry is stored. +// +// :note: code must not use this function and should use +// :func:`add_ptr_list_tag` instead. +void **__add_ptr_list_tag(struct ptr_list **listp, void *ptr, unsigned long tag) +{ + /* The low two bits are reserved for tags */ + assert((3 & (unsigned long)ptr) == 0); + assert((~3 & tag) == 0); + + ptr = (void *)(tag | (unsigned long)ptr); + + return __add_ptr_list(listp, ptr); +} + +/// +// test if some entry is already present in a ptrlist +// @list: the head of the list +// @entry: the entry to test +// @return: ``true`` if the entry is already present, ``false`` otherwise. +bool lookup_ptr_list_entry(const struct ptr_list *head, const void *entry) +{ + const struct ptr_list *list = head; + + if (!head) + return false; + do { + int nr = list->nr; + int i; + for (i = 0; i < nr; i++) + if (list->list[i] == entry) + return true; + } while ((list = list->next) != head); + return false; +} + +/// +// delete an entry from a ptrlist +// @list: a pointer to the list +// @entry: the item to be deleted +// @count: the minimum number of times @entry should be deleted or 0. int delete_ptr_list_entry(struct ptr_list **list, void *entry, int count) { void *ptr; @@ -172,7 +344,14 @@ out: return count; } -int replace_ptr_list_entry(struct ptr_list **list, void *old_ptr, void *new_ptr, int count) +/// +// replace an entry in a ptrlist +// @list: a pointer to the list +// @old_ptr: the entry to be replaced +// @new_ptr: the new entry +// @count: the minimum number of times @entry should be deleted or 0. +int replace_ptr_list_entry(struct ptr_list **list, void *old_ptr, + void *new_ptr, int count) { void *ptr; @@ -188,7 +367,12 @@ out: return count; } -/* This removes the last entry, but doesn't pack the ptr list */ +/// +// remove the last entry of a ptrlist +// @head: a pointer to the list +// @return: the last elemant of the list or NULL if the list is empty. +// +// :note: this doesn't repack the list void * undo_ptr_list_last(struct ptr_list **head) { struct ptr_list *last, *first = *head; @@ -209,6 +393,10 @@ void * undo_ptr_list_last(struct ptr_list **head) return NULL; } +/// +// remove the last entry and repack the list +// @head: a pointer to the list +// @return: the last elemant of the list or NULL if the list is empty. void * delete_ptr_list_last(struct ptr_list **head) { void *ptr = NULL; @@ -229,6 +417,11 @@ void * delete_ptr_list_last(struct ptr_list **head) return ptr; } +/// +// concat two ptrlists +// @a: the source list +// @b: a pointer to the destination list. +// The element of @a are added at the end of @b. void concat_ptr_list(struct ptr_list *a, struct ptr_list **b) { void *entry; @@ -237,6 +430,63 @@ void concat_ptr_list(struct ptr_list *a, struct ptr_list **b) } END_FOR_EACH_PTR(entry); } +/// +// copy the elements of a list at the end of another list. +// @listp: a pointer to the destination list. +// @src: the head of the source list. +void copy_ptr_list(struct ptr_list **listp, struct ptr_list *src) +{ + struct ptr_list *head, *tail; + struct ptr_list *cur = src; + int idx; + + if (!src) + return; + head = *listp; + if (!head) { + *listp = src; + return; + } + + tail = head->prev; + idx = tail->nr; + do { + struct ptr_list *next; + int nr = cur->nr; + int i; + for (i = 0; i < nr;) { + void *ptr = cur->list[i++]; + if (!ptr) + continue; + if (idx >= LIST_NODE_NR) { + struct ptr_list *prev = tail; + tail = __alloc_ptrlist(0); + prev->next = tail; + tail->prev = prev; + prev->nr = idx; + idx = 0; + } + tail->list[idx++] = ptr; + } + + next = cur->next; + __free_ptrlist(cur); + cur = next; + } while (cur != src); + + tail->nr = idx; + head->prev = tail; + tail->next = head; +} + +/// +// free a ptrlist +// @listp: a pointer to the list +// Each blocks of the list are freed (but the entries +// themselves are not freed). +// +// :note: code must not use this function and should use +// the macro :func:`free_ptr_list` instead. void __free_ptr_list(struct ptr_list **listp) { struct ptr_list *tmp, *list = *listp; diff --git a/usr/src/tools/smatch/src/ptrlist.h b/usr/src/tools/smatch/src/ptrlist.h index 78625c8d87..2411e745ab 100644 --- a/usr/src/tools/smatch/src/ptrlist.h +++ b/usr/src/tools/smatch/src/ptrlist.h @@ -2,6 +2,7 @@ #define PTR_LIST_H #include <stdlib.h> +#include <stdbool.h> /* * Generic pointer list manipulation code. @@ -10,41 +11,41 @@ */ /* Silly type-safety check ;) */ -#define DECLARE_PTR_LIST(listname,type) struct listname { type *list[1]; } #define CHECK_TYPE(head,ptr) (void)(&(ptr) == &(head)->list[0]) #define TYPEOF(head) __typeof__(&(head)->list[0]) #define VRFY_PTR_LIST(head) (void)(sizeof((head)->list[0])) -/* - * The "unnecessary" statement expression is there to shut up a totally - * bogus gcc warning about unused expressions, brought on by the fact - * that we cast the result to the proper type. - */ -#define MKTYPE(head,expr) ({ (TYPEOF(head))(expr); }) +#define LIST_NODE_NR (13) -#define LIST_NODE_NR (29) +#define DECLARE_PTR_LIST(listname, type) \ + struct listname { \ + int nr:8; \ + int rm:8; \ + struct listname *prev; \ + struct listname *next; \ + type *list[LIST_NODE_NR]; \ + } -struct ptr_list { - int nr:8; - int rm:8; - struct ptr_list *prev; - struct ptr_list *next; - void *list[LIST_NODE_NR]; -}; +DECLARE_PTR_LIST(ptr_list, void); -#define ptr_list_empty(x) ((x) == NULL) void * undo_ptr_list_last(struct ptr_list **head); void * delete_ptr_list_last(struct ptr_list **head); int delete_ptr_list_entry(struct ptr_list **, void *, int); int replace_ptr_list_entry(struct ptr_list **, void *old, void *new, int); +bool lookup_ptr_list_entry(const struct ptr_list *head, const void *entry); extern void sort_list(struct ptr_list **, int (*)(const void *, const void *)); -extern void **__add_ptr_list(struct ptr_list **, void *, unsigned long); extern void concat_ptr_list(struct ptr_list *a, struct ptr_list **b); -extern void __free_ptr_list(struct ptr_list **); +extern void copy_ptr_list(struct ptr_list **h, struct ptr_list *t); extern int ptr_list_size(struct ptr_list *); +extern bool ptr_list_empty(const struct ptr_list *head); +extern bool ptr_list_multiple(const struct ptr_list *head); extern int linearize_ptr_list(struct ptr_list *, void **, int); +extern void *first_ptr_list(struct ptr_list *); +extern void *last_ptr_list(struct ptr_list *); +extern void *ptr_list_nth_entry(struct ptr_list *, unsigned int idx); +extern void pack_ptr_list(struct ptr_list **); /* * Hey, who said that you can't do overloading in C? @@ -52,256 +53,230 @@ extern int linearize_ptr_list(struct ptr_list *, void **, int); * You just have to be creative, and use some gcc * extensions.. */ -#define add_ptr_list_tag(list,entry,tag) \ - MKTYPE(*(list), (CHECK_TYPE(*(list),(entry)),__add_ptr_list((struct ptr_list **)(list), (entry), (tag)))) -#define add_ptr_list_notag(list,entry) \ - MKTYPE(*(list), (CHECK_TYPE(*(list),(entry)),__add_ptr_list((struct ptr_list **)(list), \ - (void *)((unsigned long)(entry) & ~3UL), \ - (unsigned long)(entry) & 3))) -#define add_ptr_list(list,entry) \ - add_ptr_list_tag(list,entry,0) -#define free_ptr_list(list) \ - do { VRFY_PTR_LIST(*(list)); __free_ptr_list((struct ptr_list **)(list)); } while (0) - -#define PTR_ENTRY_NOTAG(h,i) ((h)->list[i]) -#define PTR_ENTRY(h,i) (void *)(~3UL & (unsigned long)PTR_ENTRY_NOTAG(h,i)) - -static inline void *first_ptr_list(struct ptr_list *list) -{ - struct ptr_list *head = list; +extern void **__add_ptr_list(struct ptr_list **, void *); +extern void **__add_ptr_list_tag(struct ptr_list **, void *, unsigned long); + +#define add_ptr_list(list, ptr) ({ \ + struct ptr_list** head = (struct ptr_list**)(list); \ + CHECK_TYPE(*(list),ptr); \ + (__typeof__(&(ptr))) __add_ptr_list(head, ptr); \ + }) +#define add_ptr_list_tag(list, ptr, tag) ({ \ + struct ptr_list** head = (struct ptr_list**)(list); \ + CHECK_TYPE(*(list),ptr); \ + (__typeof__(&(ptr))) __add_ptr_list_tag(head, ptr, tag);\ + }) - if (!list) - return NULL; - - while (list->nr == 0) { - list = list->next; - if (list == head) - return NULL; - } - return PTR_ENTRY(list, 0); -} - -static inline void *last_ptr_list(struct ptr_list *list) -{ - struct ptr_list *head = list; - - if (!list) - return NULL; - list = list->prev; - while (list->nr == 0) { - if (list == head) - return NULL; - list = list->prev; - } - return PTR_ENTRY(list, list->nr-1); -} - -#define PTR_DEREF(__head, idx, PTR_ENTRY) ({ \ - struct ptr_list *__list = __head; \ - while (__list && __list->nr == 0) { \ - __list = __list->next; \ - if (__list == __head) \ - __list = NULL; \ - } \ - __list ? PTR_ENTRY(__list, idx) : NULL; \ -}) - -#define DO_PREPARE(head, ptr, __head, __list, __nr, PTR_ENTRY) \ - do { \ - struct ptr_list *__head = (struct ptr_list *) (head); \ - struct ptr_list *__list = __head; \ - int __nr = 0; \ - CHECK_TYPE(head,ptr); \ - ptr = PTR_DEREF(__head, 0, PTR_ENTRY); \ - -#define DO_NEXT(ptr, __head, __list, __nr, PTR_ENTRY) \ - if (ptr) { \ - if (++__nr < __list->nr) { \ - ptr = PTR_ENTRY(__list,__nr); \ - } else { \ - __list = __list->next; \ - ptr = NULL; \ - while (__list->nr == 0 && __list != __head) \ - __list = __list->next; \ - if (__list != __head) { \ - __nr = 0; \ - ptr = PTR_ENTRY(__list,0); \ - } \ - } \ - } - -#define DO_RESET(ptr, __head, __list, __nr, PTR_ENTRY) \ - do { \ - __nr = 0; \ - __list = __head; \ - if (__head) ptr = PTR_DEREF(__head, 0, PTR_ENTRY); \ +extern void __free_ptr_list(struct ptr_list **); +#define free_ptr_list(list) do { \ + VRFY_PTR_LIST(*(list)); \ + __free_ptr_list((struct ptr_list **)(list)); \ } while (0) -#define DO_FINISH(ptr, __head, __list, __nr) \ - (void)(__nr); /* Sanity-check nesting */ \ - } while (0) +//////////////////////////////////////////////////////////////////////// +// API #define PREPARE_PTR_LIST(head, ptr) \ - DO_PREPARE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY) + DO_PREPARE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG) #define NEXT_PTR_LIST(ptr) \ - DO_NEXT(ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY) + DO_NEXT(ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG) #define RESET_PTR_LIST(ptr) \ - DO_RESET(ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY) + DO_RESET(ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG) #define FINISH_PTR_LIST(ptr) \ DO_FINISH(ptr, __head##ptr, __list##ptr, __nr##ptr) -#define DO_FOR_EACH(head, ptr, __head, __list, __nr, PTR_ENTRY) do { \ - struct ptr_list *__head = (struct ptr_list *) (head); \ - struct ptr_list *__list = __head; \ - CHECK_TYPE(head,ptr); \ - if (__head) { \ - do { int __nr; \ - for (__nr = 0; __nr < __list->nr; __nr++) { \ - do { \ - ptr = PTR_ENTRY(__list,__nr); \ - if (__list->rm && !ptr) \ - continue; \ - do { - -#define DO_END_FOR_EACH(ptr, __head, __list, __nr) \ - } while (0); \ - } while (0); \ - } \ - } while ((__list = __list->next) != __head); \ - } \ -} while (0) +#define RECURSE_PTR_REVERSE(ptr, new) \ + DO_REVERSE(ptr, __head##ptr, __list##ptr, __nr##ptr, \ + new, __head##new, __list##new, __nr##new, PTR_ENTRY_UNTAG) -#define DO_FOR_EACH_REVERSE(head, ptr, __head, __list, __nr, PTR_ENTRY) do { \ - struct ptr_list *__head = (struct ptr_list *) (head); \ - struct ptr_list *__list = __head; \ - CHECK_TYPE(head,ptr); \ - if (__head) { \ - do { int __nr; \ - __list = __list->prev; \ - __nr = __list->nr; \ - while (--__nr >= 0) { \ - do { \ - ptr = PTR_ENTRY(__list,__nr); \ - if (__list->rm && !ptr) \ - continue; \ - do { - - -#define DO_END_FOR_EACH_REVERSE(ptr, __head, __list, __nr) \ - } while (0); \ - } while (0); \ - } \ - } while (__list != __head); \ - } \ -} while (0) - -#define DO_REVERSE(ptr, __head, __list, __nr, new, __newhead, \ - __newlist, __newnr, PTR_ENTRY) do { \ - struct ptr_list *__newhead = __head; \ - struct ptr_list *__newlist = __list; \ - int __newnr = __nr; \ - new = ptr; \ - goto __inside##new; \ - if (1) { \ - do { \ - __newlist = __newlist->prev; \ - __newnr = __newlist->nr; \ - __inside##new: \ - while (--__newnr >= 0) { \ - do { \ - new = PTR_ENTRY(__newlist,__newnr); \ - do { - -#define RECURSE_PTR_REVERSE(ptr, new) \ - DO_REVERSE(ptr, __head##ptr, __list##ptr, __nr##ptr, \ - new, __head##new, __list##new, __nr##new, PTR_ENTRY) - -#define DO_THIS_ADDRESS(ptr, __head, __list, __nr) \ - ((__typeof__(&(ptr))) (__list->list + __nr)) #define FOR_EACH_PTR(head, ptr) \ - DO_FOR_EACH(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY) + DO_FOR_EACH(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_NOTAG) + +#define FOR_EACH_PTR_TAG(head, ptr) \ + DO_FOR_EACH(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG) #define END_FOR_EACH_PTR(ptr) \ DO_END_FOR_EACH(ptr, __head##ptr, __list##ptr, __nr##ptr) -#define FOR_EACH_PTR_NOTAG(head, ptr) \ - DO_FOR_EACH(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_NOTAG) - -#define END_FOR_EACH_PTR_NOTAG(ptr) END_FOR_EACH_PTR(ptr) - #define FOR_EACH_PTR_REVERSE(head, ptr) \ - DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY) + DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_NOTAG) + +#define FOR_EACH_PTR_REVERSE_TAG(head, ptr) \ + DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG) #define END_FOR_EACH_PTR_REVERSE(ptr) \ DO_END_FOR_EACH_REVERSE(ptr, __head##ptr, __list##ptr, __nr##ptr) -#define FOR_EACH_PTR_REVERSE_NOTAG(head, ptr) \ - DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_NOTAG) - -#define END_FOR_EACH_PTR_REVERSE_NOTAG(ptr) END_FOR_EACH_PTR_REVERSE(ptr) - #define THIS_ADDRESS(ptr) \ DO_THIS_ADDRESS(ptr, __head##ptr, __list##ptr, __nr##ptr) -extern void split_ptr_list_head(struct ptr_list *); +#define INSERT_CURRENT(new, ptr) \ + DO_INSERT_CURRENT(new, __head##ptr, __list##ptr, __nr##ptr) -#define DO_SPLIT(ptr, __head, __list, __nr) do { \ - split_ptr_list_head(__list); \ - if (__nr >= __list->nr) { \ - __nr -= __list->nr; \ - __list = __list->next; \ - }; \ -} while (0) +#define DELETE_CURRENT_PTR(ptr) \ + DO_DELETE_CURRENT(__head##ptr, __list##ptr, __nr##ptr) + +#define REPLACE_CURRENT_PTR(ptr, new_ptr) \ + do { *THIS_ADDRESS(ptr) = (new_ptr); } while (0) + +// This replace the current element by a null-pointer. +// It's used when an element of the list must be removed +// but the address of the other elements must not be changed. +#define MARK_CURRENT_DELETED(ptr) \ + DO_MARK_CURRENT_DELETED(ptr, __list##ptr) + +#define PACK_PTR_LIST(x) \ + pack_ptr_list((struct ptr_list **)(x)) -#define DO_INSERT_CURRENT(new, ptr, __head, __list, __nr) do { \ - void **__this, **__last; \ - if (__list->nr == LIST_NODE_NR) \ - DO_SPLIT(ptr, __head, __list, __nr); \ - __this = __list->list + __nr; \ - __last = __list->list + __list->nr - 1; \ - while (__last >= __this) { \ - __last[1] = __last[0]; \ - __last--; \ - } \ - *__this = (new); \ - __list->nr++; \ +#define CURRENT_TAG(ptr) (3 & (unsigned long)*THIS_ADDRESS(ptr)) +#define TAG_CURRENT(ptr,val) update_tag(THIS_ADDRESS(ptr),val) + +// backward compatibility for smatch +#define FOR_EACH_PTR_NOTAG(list, ptr) FOR_EACH_PTR(list, ptr) +#define END_FOR_EACH_PTR_NOTAG(ptr) END_FOR_EACH_PTR(ptr) + +//////////////////////////////////////////////////////////////////////// +// Implementation +#define PTR_UNTAG(p) ((void*)(~3UL & (unsigned long)(p))) +#define PTR_ENTRY_NOTAG(h,i) ((h)->list[i]) +#define PTR_ENTRY_UNTAG(h,i) PTR_UNTAG((h)->list[i]) + + +#define PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY) \ + do { \ + if (__nr < __list->nr) { \ + ptr = PTR_ENTRY(__list,__nr); \ + __nr++; \ + break; \ + } \ + ptr = NULL; \ + __nr = 0; \ + } while ((__list = __list->next) != __head) \ + +#define DO_PREPARE(head, ptr, __head, __list, __nr, PTR_ENTRY) \ + do { \ + __typeof__(head) __head = (head); \ + __typeof__(head) __list = __head; \ + int __nr = 0; \ + ptr = NULL; \ + if (__head) { \ + PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY); \ + } \ + +#define DO_NEXT(ptr, __head, __list, __nr, PTR_ENTRY) \ + if (ptr) { \ + PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY); \ + } + +#define DO_RESET(ptr, __head, __list, __nr, PTR_ENTRY) \ + do { \ + __nr = 0; \ + __list = __head; \ + if (__head) \ + PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY); \ + } while (0) + +#define DO_FINISH(ptr, __head, __list, __nr) \ + VRFY_PTR_LIST(__head); /* Sanity-check nesting */ \ + } while (0) + +#define DO_FOR_EACH(head, ptr, __head, __list, __nr, PTR_ENTRY) do { \ + __typeof__(head) __head = (head); \ + __typeof__(head) __list = __head; \ + int __nr; \ + if (!__head) \ + break; \ + do { \ + for (__nr = 0; __nr < __list->nr; __nr++) { \ + ptr = PTR_ENTRY(__list,__nr); \ + if (__list->rm && !ptr) \ + continue; \ + +#define DO_END_FOR_EACH(ptr, __head, __list, __nr) \ + } \ + } while ((__list = __list->next) != __head); \ } while (0) -#define INSERT_CURRENT(new, ptr) \ - DO_INSERT_CURRENT(new, ptr, __head##ptr, __list##ptr, __nr##ptr) - -#define DO_DELETE_CURRENT(ptr, __head, __list, __nr) do { \ - void **__this = __list->list + __nr; \ - void **__last = __list->list + __list->nr - 1; \ - while (__this < __last) { \ - __this[0] = __this[1]; \ - __this++; \ - } \ - *__this = (void *)0xf0f0f0f0; \ - __list->nr--; __nr--; \ +#define DO_FOR_EACH_REVERSE(head, ptr, __head, __list, __nr, PTR_ENTRY) do { \ + __typeof__(head) __head = (head); \ + __typeof__(head) __list = __head; \ + int __nr; \ + if (!head) \ + break; \ + do { \ + __list = __list->prev; \ + __nr = __list->nr; \ + while (--__nr >= 0) { \ + ptr = PTR_ENTRY(__list,__nr); \ + if (__list->rm && !ptr) \ + continue; \ + + +#define DO_END_FOR_EACH_REVERSE(ptr, __head, __list, __nr) \ + } \ + } while (__list != __head); \ } while (0) -#define DELETE_CURRENT_PTR(ptr) \ - DO_DELETE_CURRENT(ptr, __head##ptr, __list##ptr, __nr##ptr) +#define DO_REVERSE(ptr, __head, __list, __nr, new, __newhead, \ + __newlist, __newnr, PTR_ENTRY) do { \ + __typeof__(__head) __newhead = __head; \ + __typeof__(__head) __newlist = __list; \ + int __newnr = __nr; \ + new = ptr; \ + goto __inside##new; \ + do { \ + __newlist = __newlist->prev; \ + __newnr = __newlist->nr; \ + __inside##new: \ + while (--__newnr >= 0) { \ + new = PTR_ENTRY(__newlist,__newnr); \ -#define REPLACE_CURRENT_PTR(ptr, new_ptr) \ - do { *THIS_ADDRESS(ptr) = (new_ptr); } while (0) +#define DO_THIS_ADDRESS(ptr, __head, __list, __nr) \ + (&__list->list[__nr]) -#define DO_MARK_CURRENT_DELETED(ptr, __list) do { \ - REPLACE_CURRENT_PTR(ptr, NULL); \ - __list->rm++; \ - } while (0) -#define MARK_CURRENT_DELETED(ptr) \ - DO_MARK_CURRENT_DELETED(ptr, __list##ptr) +extern void split_ptr_list_head(struct ptr_list *); + +#define DO_INSERT_CURRENT(new, __head, __list, __nr) do { \ + TYPEOF(__head) __this, __last; \ + if (__list->nr == LIST_NODE_NR) { \ + split_ptr_list_head((struct ptr_list*)__list); \ + if (__nr >= __list->nr) { \ + __nr -= __list->nr; \ + __list = __list->next; \ + } \ + } \ + __this = __list->list + __nr; \ + __last = __list->list + __list->nr - 1; \ + while (__last >= __this) { \ + __last[1] = __last[0]; \ + __last--; \ + } \ + *__this = (new); \ + __list->nr++; \ +} while (0) + +#define DO_DELETE_CURRENT(__head, __list, __nr) do { \ + TYPEOF(__head) __this = __list->list + __nr; \ + TYPEOF(__head) __last = __list->list + __list->nr - 1; \ + while (__this < __last) { \ + __this[0] = __this[1]; \ + __this++; \ + } \ + *__this = (void *)0xf0f0f0f0; \ + __list->nr--; __nr--; \ +} while (0) -extern void pack_ptr_list(struct ptr_list **); -#define PACK_PTR_LIST(x) pack_ptr_list((struct ptr_list **)(x)) +#define DO_MARK_CURRENT_DELETED(ptr, __list) do { \ + REPLACE_CURRENT_PTR(ptr, NULL); \ + __list->rm++; \ + } while (0) + static inline void update_tag(void *p, unsigned long tag) { @@ -314,7 +289,4 @@ static inline void *tag_ptr(void *ptr, unsigned long tag) return (void *)(tag | (unsigned long)ptr); } -#define CURRENT_TAG(ptr) (3 & (unsigned long)*THIS_ADDRESS(ptr)) -#define TAG_CURRENT(ptr,val) update_tag(THIS_ADDRESS(ptr),val) - #endif /* PTR_LIST_H */ diff --git a/usr/src/tools/smatch/src/ptrmap.c b/usr/src/tools/smatch/src/ptrmap.c new file mode 100644 index 0000000000..ff07e19a1e --- /dev/null +++ b/usr/src/tools/smatch/src/ptrmap.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +/* + * Stupid implementation of pointer -> pointer map. + * + * Copyright (c) 2017 Luc Van Oostenryck. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "ptrmap.h" +#include "allocate.h" +#include <stddef.h> + +#define MAP_NR 7 + +struct ptrpair { + void *key; + void *val; +}; +struct ptrmap { + struct ptrmap *next; + int nr; + struct ptrpair pairs[MAP_NR]; +}; + +DECLARE_ALLOCATOR(ptrmap); +ALLOCATOR(ptrmap, "ptrmap"); + +void __ptrmap_add(struct ptrmap **mapp, void *key, void *val) +{ + struct ptrmap *head = *mapp; + struct ptrmap *newmap; + struct ptrmap *map; + struct ptrpair *pair; + int nr; + + if ((map = head)) { + struct ptrmap *next = map->next; + if (next) // head is full + map = next; + if ((nr = map->nr) < MAP_NR) + goto oldmap; + } + + // need a new block + newmap = __alloc_ptrmap(0); + if (!head) { + *mapp = newmap; + } else { + newmap->next = head->next; + head->next = newmap; + } + map = newmap; + nr = 0; + +oldmap: + pair = &map->pairs[nr]; + pair->key = key; + pair->val = val; + map->nr = ++nr; +} + +void *__ptrmap_lookup(struct ptrmap *map, void *key) +{ + for (; map; map = map->next) { + int i, n = map->nr; + for (i = 0; i < n; i++) { + struct ptrpair *pair = &map->pairs[i]; + if (pair->key == key) + return pair->val; + } + } + return NULL; +} + +void __ptrmap_update(struct ptrmap **mapp, void *key, void *val) +{ + struct ptrmap *map = *mapp; + + for (; map; map = map->next) { + int i, n = map->nr; + for (i = 0; i < n; i++) { + struct ptrpair *pair = &map->pairs[i]; + if (pair->key == key) { + if (pair->val != val) + pair->val = val; + return; + } + } + } + + __ptrmap_add(mapp, key, val); +} diff --git a/usr/src/tools/smatch/src/ptrmap.h b/usr/src/tools/smatch/src/ptrmap.h new file mode 100644 index 0000000000..cbbb61da96 --- /dev/null +++ b/usr/src/tools/smatch/src/ptrmap.h @@ -0,0 +1,28 @@ +#ifndef PTRMAP_H +#define PTRMAP_H + +struct ptrmap; + +#define DECLARE_PTRMAP(name, ktype, vtype) \ + struct name ## _pair { ktype key; vtype val; }; \ + struct name { struct name ## _pair block[1]; }; \ + static inline \ + void name##_add(struct name **map, ktype k, vtype v) { \ + __ptrmap_add((struct ptrmap**)map, k, v); \ + } \ + static inline \ + void name##_update(struct name **map, ktype k, vtype v) { \ + __ptrmap_update((struct ptrmap**)map, k, v); \ + } \ + static inline \ + vtype name##_lookup(struct name *map, ktype k) { \ + vtype val = __ptrmap_lookup((struct ptrmap*)map, k); \ + return val; \ + } \ + +/* ptrmap.c */ +void __ptrmap_add(struct ptrmap **mapp, void *key, void *val); +void __ptrmap_update(struct ptrmap **mapp, void *key, void *val); +void *__ptrmap_lookup(struct ptrmap *map, void *key); + +#endif diff --git a/usr/src/tools/smatch/src/scope.h b/usr/src/tools/smatch/src/scope.h index 10118e5655..b0e90374fe 100644 --- a/usr/src/tools/smatch/src/scope.h +++ b/usr/src/tools/smatch/src/scope.h @@ -29,7 +29,7 @@ struct symbol; struct position; struct scope { - struct token *token; /* Scope start information */ + struct token *token; struct symbol_list *symbols; /* List of symbols in this scope */ struct scope *next; }; diff --git a/usr/src/tools/smatch/src/show-parse.c b/usr/src/tools/smatch/src/show-parse.c index d365d737f1..3aa06e47cf 100644 --- a/usr/src/tools/smatch/src/show-parse.c +++ b/usr/src/tools/smatch/src/show-parse.c @@ -72,10 +72,11 @@ static void do_debug_symbol(struct symbol *sym, int indent) if (!sym) return; - fprintf(stderr, "%.*s%s%3d:%lu %s %s (as: %d) %p (%s:%d:%d) %s\n", + fprintf(stderr, "%.*s%s%3d:%lu %s %s (as: %s) %p (%s:%d:%d) %s\n", indent, indent_string, typestr[sym->type], sym->bit_size, sym->ctype.alignment, - modifier_string(sym->ctype.modifiers), show_ident(sym->ident), sym->ctype.as, + modifier_string(sym->ctype.modifiers), show_ident(sym->ident), + show_as(sym->ctype.as), sym, stream_name(sym->pos.stream), sym->pos.line, sym->pos.pos, builtin_typename(sym) ?: ""); i = 0; @@ -125,6 +126,8 @@ const char *modifier_string(unsigned long mod) {MOD_EXTERN, "extern"}, {MOD_CONST, "const"}, {MOD_VOLATILE, "volatile"}, + {MOD_RESTRICT, "restrict"}, + {MOD_ATOMIC, "[atomic]"}, {MOD_SIGNED, "[signed]"}, {MOD_UNSIGNED, "[unsigned]"}, {MOD_CHAR, "[char]"}, @@ -132,13 +135,11 @@ const char *modifier_string(unsigned long mod) {MOD_LONG, "[long]"}, {MOD_LONGLONG, "[long long]"}, {MOD_LONGLONGLONG, "[long long long]"}, - {MOD_TYPEDEF, "[typedef]"}, {MOD_TLS, "[tls]"}, {MOD_INLINE, "inline"}, {MOD_ADDRESSABLE, "[addressable]"}, {MOD_NOCAST, "[nocast]"}, {MOD_NODEREF, "[noderef]"}, - {MOD_ACCESSED, "[accessed]"}, {MOD_TOPLEVEL, "[toplevel]"}, {MOD_ASSIGNED, "[assigned]"}, {MOD_TYPE, "[type]"}, @@ -182,6 +183,13 @@ void show_symbol_list(struct symbol_list *list, const char *sep) } END_FOR_EACH_PTR(sym); } +const char *show_as(struct ident *as) +{ + if (!as) + return ""; + return show_ident(as); +} + struct type_name { char *start; char *end; @@ -218,38 +226,38 @@ static void FORMAT_ATTR(2) append(struct type_name *name, const char *fmt, ...) static struct ctype_name { struct symbol *sym; const char *name; + const char *suffix; } typenames[] = { - { & char_ctype, "char" }, - { &schar_ctype, "signed char" }, - { &uchar_ctype, "unsigned char" }, - { & short_ctype, "short" }, - { &sshort_ctype, "signed short" }, - { &ushort_ctype, "unsigned short" }, - { & int_ctype, "int" }, - { &sint_ctype, "signed int" }, - { &uint_ctype, "unsigned int" }, - { &slong_ctype, "signed long" }, - { & long_ctype, "long" }, - { &ulong_ctype, "unsigned long" }, - { & llong_ctype, "long long" }, - { &sllong_ctype, "signed long long" }, - { &ullong_ctype, "unsigned long long" }, - { & lllong_ctype, "long long long" }, - { &slllong_ctype, "signed long long long" }, - { &ulllong_ctype, "unsigned long long long" }, - - { &void_ctype, "void" }, - { &bool_ctype, "bool" }, - { &string_ctype, "string" }, - - { &float_ctype, "float" }, - { &double_ctype, "double" }, - { &ldouble_ctype,"long double" }, - { &incomplete_ctype, "incomplete type" }, - { &int_type, "abstract int" }, - { &fp_type, "abstract fp" }, - { &label_ctype, "label type" }, - { &bad_ctype, "bad type" }, + { & char_ctype, "char", "" }, + { &schar_ctype, "signed char", "" }, + { &uchar_ctype, "unsigned char", "" }, + { & short_ctype, "short", "" }, + { &sshort_ctype, "signed short", "" }, + { &ushort_ctype, "unsigned short", "" }, + { & int_ctype, "int", "" }, + { &sint_ctype, "signed int", "" }, + { &uint_ctype, "unsigned int", "U" }, + { & long_ctype, "long", "L" }, + { &slong_ctype, "signed long", "L" }, + { &ulong_ctype, "unsigned long", "UL" }, + { & llong_ctype, "long long", "LL" }, + { &sllong_ctype, "signed long long", "LL" }, + { &ullong_ctype, "unsigned long long", "ULL" }, + { & lllong_ctype, "long long long", "LLL" }, + { &slllong_ctype, "signed long long long", "LLL" }, + { &ulllong_ctype, "unsigned long long long", "ULLL" }, + + { &void_ctype, "void", "" }, + { &bool_ctype, "bool", "" }, + + { &float_ctype, "float", "F" }, + { &double_ctype, "double", "" }, + { &ldouble_ctype,"long double", "L" }, + { &incomplete_ctype, "incomplete type", "" }, + { &int_type, "abstract int", "" }, + { &fp_type, "abstract fp", "" }, + { &label_ctype, "label type", "" }, + { &bad_ctype, "bad type", "" }, }; const char *builtin_typename(struct symbol *sym) @@ -262,6 +270,16 @@ const char *builtin_typename(struct symbol *sym) return NULL; } +const char *builtin_type_suffix(struct symbol *sym) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(typenames); i++) + if (typenames[i].sym == sym) + return typenames[i].suffix; + return NULL; +} + const char *builtin_ctypename(struct ctype *ctype) { int i; @@ -276,7 +294,7 @@ static void do_show_type(struct symbol *sym, struct type_name *name) { const char *typename; unsigned long mod = 0; - int as = 0; + struct ident *as = NULL; int was_ptr = 0; int restr = 0; int fouled = 0; @@ -288,14 +306,16 @@ deeper: size_t len; if (as) - prepend(name, "<asn:%d>", as); + prepend(name, "%s ", show_as(as)); + if (sym->type == SYM_BASETYPE || sym->type == SYM_ENUM) + mod &= ~MOD_SPECIFIER; s = modifier_string(mod); len = strlen(s); name->start -= len; memcpy(name->start, s, len); mod = 0; - as = 0; + as = NULL; } if (!sym) @@ -345,14 +365,15 @@ deeper: break; case SYM_NODE: - append(name, "%s", show_ident(sym->ident)); + if (sym->ident) + append(name, "%s", show_ident(sym->ident)); mod |= sym->ctype.modifiers; - as |= sym->ctype.as; + combine_address_space(sym->pos, &as, sym->ctype.as); break; case SYM_BITFIELD: mod |= sym->ctype.modifiers; - as |= sym->ctype.as; + combine_address_space(sym->pos, &as, sym->ctype.as); append(name, ":%d", sym->bit_size); break; @@ -362,7 +383,7 @@ deeper: case SYM_ARRAY: mod |= sym->ctype.modifiers; - as |= sym->ctype.as; + combine_address_space(sym->pos, &as, sym->ctype.as); if (was_ptr) { prepend(name, "( "); append(name, " )"); @@ -400,6 +421,10 @@ out: prepend(name, "restricted "); if (fouled) prepend(name, "fouled "); + + // strip trailing space + if (name->end > name->start && name->end[-1] == ' ') + name->end--; } void show_type(struct symbol *sym) @@ -927,7 +952,7 @@ static int show_symbol_expr(struct symbol *sym) return new; } if (sym->ctype.modifiers & MOD_ADDRESSABLE) { - printf("\taddi.%d\t\tv%d,vFP,$%lld\n", bits_in_pointer, new, sym->value); + printf("\taddi.%d\t\tv%d,vFP,$%lld\n", bits_in_pointer, new, 0LL); return new; } printf("\taddi.%d\t\tv%d,vFP,$offsetof(%s:%p)\n", bits_in_pointer, new, show_ident(sym->ident), sym); @@ -949,15 +974,6 @@ static int show_symbol_init(struct symbol *sym) return 0; } -static int type_is_signed(struct symbol *sym) -{ - if (sym->type == SYM_NODE) - sym = sym->ctype.base_type; - if (sym->type == SYM_PTR) - return 0; - return !(sym->ctype.modifiers & MOD_UNSIGNED); -} - static int show_cast_expr(struct expression *expr) { struct symbol *old_type, *new_type; @@ -973,7 +989,7 @@ static int show_cast_expr(struct expression *expr) if (oldbits >= newbits) return op; new = new_pseudo(); - is_signed = type_is_signed(old_type); + is_signed = is_signed_type(old_type); if (is_signed) { printf("\tsext%d.%d\tv%d,v%d\n", oldbits, newbits, new, op); } else { @@ -996,7 +1012,7 @@ static int show_fvalue(struct expression *expr) int new = new_pseudo(); long double value = expr->fvalue; - printf("\tmovf.%d\t\tv%d,$%Lf\n", expr->ctype->bit_size, new, value); + printf("\tmovf.%d\t\tv%d,$%Le\n", expr->ctype->bit_size, new, value); return new; } @@ -1018,11 +1034,11 @@ static int show_label_expr(struct expression *expr) static int show_conditional_expr(struct expression *expr) { int cond = show_expression(expr->conditional); - int true = show_expression(expr->cond_true); - int false = show_expression(expr->cond_false); + int valt = show_expression(expr->cond_true); + int valf = show_expression(expr->cond_false); int new = new_pseudo(); - printf("[v%d]\tcmov.%d\t\tv%d,v%d,v%d\n", cond, expr->ctype->bit_size, new, true, false); + printf("[v%d]\tcmov.%d\t\tv%d,v%d,v%d\n", cond, expr->ctype->bit_size, new, valt, valf); return new; } @@ -1169,6 +1185,9 @@ int show_expression(struct expression *expr) case EXPR_TYPE: warning(expr->pos, "unable to show type expression"); return 0; + case EXPR_ASM_OPERAND: + warning(expr->pos, "unable to show asm operand expression"); + return 0; } return 0; } diff --git a/usr/src/tools/smatch/src/simplify.c b/usr/src/tools/smatch/src/simplify.c index 1e926e7d6e..7850bcdc60 100644 --- a/usr/src/tools/smatch/src/simplify.c +++ b/usr/src/tools/smatch/src/simplify.c @@ -4,6 +4,41 @@ * Copyright (C) 2004 Linus Torvalds */ +/// +// Instruction simplification +// -------------------------- +// +// Notation +// ^^^^^^^^ +// The following conventions are used to describe the simplications: +// * Uppercase letters are reserved for constants: +// * `M` for a constant mask, +// * `S` for a constant shift, +// * `N` for a constant number of bits (usually other than a shift), +// * `C` or 'K' for others constants. +// * Lowercase letters `a`, `b`, `x`, `y`, ... are used for non-constants +// or when it doesn't matter if the pseudo is a constant or not. +// * Primes are used if needed to distinguish symbols (`M`, `M'`, ...). +// * Expressions or sub-expressions involving only constants are +// understood to be evaluated. +// * `$mask(N)` is used for `((1 << N) -1)` +// * `$trunc(x, N)` is used for `(x & $mask(N))` +// * Expressions like `(-1 << S)`, `(-1 >> S)` and others formulae are +// understood to be truncated to the size of the current instruction +// (needed, since in general this size is not the same as the one used +// by sparse for the evaluation of arithmetic operations). +// * `TRUNC(x, N)` is used for a truncation *to* a size of `N` bits +// * `ZEXT(x, N)` is used for a zero-extension *from* a size of `N` bits +// * `OP(x, C)` is used to represent some generic operation using a constant, +// including when the constant is implicit (e.g. `TRUNC(x, N)`). +// * `MASK(x, M)` is used to respresent a 'masking' instruction: +// - `AND(x, M)` +// - `LSR(x, S)`, with `M` = (-1 << S) +// - `SHL(x, S)`, with `M` = (-1 >> S) +// - `TRUNC(x, N)`, with `M` = $mask(N) +// - `ZEXT(x, N)`, with `M` = $mask(N) +// * `SHIFT(x, S)` is used for `LSR(x, S)` or `SHL(x, S)`. + #include <assert.h> #include "parse.h" @@ -12,7 +47,12 @@ #include "flow.h" #include "symbol.h" -/* Find the trivial parent for a phi-source */ +/// +// Utilities +// ^^^^^^^^^ + +/// +// find the trivial parent for a phi-source static struct basic_block *phi_parent(struct basic_block *source, pseudo_t pseudo) { /* Can't go upwards if the pseudo is defined in the bb it came from.. */ @@ -26,16 +66,15 @@ static struct basic_block *phi_parent(struct basic_block *source, pseudo_t pseud return first_basic_block(source->parents); } -/* - * Copy the phi-node's phisrcs into to given array. - * Returns 0 if the the list contained the expected - * number of element, a positive number if there was - * more than expected and a negative one if less. - * - * Note: we can't reuse a function like linearize_ptr_list() - * because any VOIDs in the phi-list must be ignored here - * as in this context they mean 'entry has been removed'. - */ +/// +// copy the phi-node's phisrcs into to given array +// @return: 0 if the the list contained the expected +// number of element, a positive number if there was +// more than expected and a negative one if less. +// +// :note: we can't reuse a function like linearize_ptr_list() +// because any VOIDs in the phi-list must be ignored here +// as in this context they mean 'entry has been removed'. static int get_phisources(struct instruction *sources[], int nbr, struct instruction *insn) { pseudo_t phi; @@ -68,9 +107,9 @@ static int if_convert_phi(struct instruction *insn) return 0; if (linearize_ptr_list((struct ptr_list *)bb->parents, (void **)parents, 3) != 2) return 0; - p1 = array[0]->src1; + p1 = array[0]->phi_src; bb1 = array[0]->bb; - p2 = array[1]->src1; + p2 = array[1]->phi_src; bb2 = array[1]->bb; /* Only try the simple "direct parents" case */ @@ -133,31 +172,64 @@ static int if_convert_phi(struct instruction *insn) return REPEAT_CSE; } -static int clean_up_phi(struct instruction *insn) +/// +// detect trivial phi-nodes +// @insn: the phi-node +// @pseudo: the candidate resulting pseudo (NULL when starting) +// @return: the unique result if the phi-node is trivial, NULL otherwise +// +// A phi-node is trivial if it has a single possible result: +// * all operands are the same +// * the operands are themselves defined by a chain or cycle of phi-nodes +// and the set of all operands involved contains a single value +// not defined by these phi-nodes +// +// Since the result is unique, these phi-nodes can be removed. +static pseudo_t trivial_phi(pseudo_t pseudo, struct instruction *insn, struct pseudo_list **list) { + pseudo_t target = insn->target; pseudo_t phi; - struct instruction *last; - int same; - last = NULL; - same = 1; + add_pseudo(list, target); + FOR_EACH_PTR(insn->phi_list, phi) { struct instruction *def; + pseudo_t src; + if (phi == VOID) continue; def = phi->def; - if (def->src1 == VOID || !def->bb) + if (!def->bb) continue; - if (last) { - if (last->src1 != def->src1) - same = 0; + src = def->phi_src; // bypass OP_PHISRC & get the real source + if (src == VOID) continue; + if (!pseudo) { + pseudo = src; + continue; + } + if (src == pseudo) + continue; + if (src == target) + continue; + if (DEF_OPCODE(def, src) == OP_PHI) { + if (pseudo_in_list(*list, src)) + continue; + if ((pseudo = trivial_phi(pseudo, def, list))) + continue; } - last = def; + return NULL; } END_FOR_EACH_PTR(phi); - if (same) { - pseudo_t pseudo = last ? last->src1 : VOID; + return pseudo ? pseudo : VOID; +} + +static int clean_up_phi(struct instruction *insn) +{ + struct pseudo_list *list = NULL; + pseudo_t pseudo; + + if ((pseudo = trivial_phi(NULL, insn, &list))) { convert_instruction_target(insn, pseudo); kill_instruction(insn); return REPEAT_CSE; @@ -179,29 +251,44 @@ static int delete_pseudo_user_list_entry(struct pseudo_user_list **list, pseudo_ } END_FOR_EACH_PTR(pu); assert(count <= 0); out: - if (ptr_list_size((struct ptr_list *) *list) == 0) + if (pseudo_user_list_empty(*list)) *list = NULL; return count; } -static inline void remove_usage(pseudo_t p, pseudo_t *usep) +static inline void rem_usage(pseudo_t p, pseudo_t *usep, int kill) { if (has_use_list(p)) { + if (p->type == PSEUDO_SYM) + repeat_phase |= REPEAT_SYMBOL_CLEANUP; delete_pseudo_user_list_entry(&p->users, usep, 1); - if (!p->users) + if (kill && !p->users) kill_instruction(p->def); } } +static inline void remove_usage(pseudo_t p, pseudo_t *usep) +{ + rem_usage(p, usep, 1); +} + void kill_use(pseudo_t *usep) { if (usep) { pseudo_t p = *usep; *usep = VOID; - remove_usage(p, usep); + rem_usage(p, usep, 1); } } +// Like kill_use() but do not (recursively) kill dead instructions +void remove_use(pseudo_t *usep) +{ + pseudo_t p = *usep; + *usep = VOID; + rem_usage(p, usep, 0); +} + static void kill_use_list(struct pseudo_list *list) { pseudo_t p; @@ -212,19 +299,20 @@ static void kill_use_list(struct pseudo_list *list) } END_FOR_EACH_PTR(p); } -/* - * kill an instruction: - * - remove it from its bb - * - remove the usage of all its operands - * If forse is zero, the normal case, the function only for - * instructions free of (possible) side-effects. Otherwise - * the function does that unconditionally (must only be used - * for unreachable instructions. - */ -void kill_insn(struct instruction *insn, int force) +/// +// kill an instruction +// @insn: the instruction to be killed +// @force: if unset, the normal case, the instruction is not killed +// if not free of possible side-effect; if set the instruction +// is unconditionally killed. +// +// The killed instruction is removed from its BB and the usage +// of all its operands are removed. The instruction is also +// marked as killed by setting its ->bb to NULL. +int kill_insn(struct instruction *insn, int force) { if (!insn || !insn->bb) - return; + return 0; switch (insn->opcode) { case OP_SEL: @@ -236,12 +324,8 @@ void kill_insn(struct instruction *insn, int force) kill_use(&insn->src2); /* fall through */ - case OP_CAST: - case OP_SCAST: - case OP_FPCAST: - case OP_PTRCAST: + case OP_UNOP ... OP_UNOP_END: case OP_SETVAL: - case OP_NOT: case OP_NEG: case OP_SLICE: kill_use(&insn->src1); break; @@ -254,10 +338,12 @@ void kill_insn(struct instruction *insn, int force) break; case OP_SYMADDR: + kill_use(&insn->src); repeat_phase |= REPEAT_SYMBOL_CLEANUP; break; case OP_CBR: + case OP_SWITCH: case OP_COMPUTEDGOTO: kill_use(&insn->cond); break; @@ -266,9 +352,9 @@ void kill_insn(struct instruction *insn, int force) if (!force) { /* a "pure" function can be killed too */ if (!(insn->func->type == PSEUDO_SYM)) - return; + return 0; if (!(insn->func->sym->ctype.modifiers & MOD_PURE)) - return; + return 0; } kill_use_list(insn->arguments); if (insn->func->type == PSEUDO_REG) @@ -276,42 +362,38 @@ void kill_insn(struct instruction *insn, int force) break; case OP_LOAD: - if (!force && insn->type->ctype.modifiers & MOD_VOLATILE) - return; + if (!force && insn->is_volatile) + return 0; kill_use(&insn->src); break; case OP_STORE: if (!force) - return; + return 0; kill_use(&insn->src); kill_use(&insn->target); break; case OP_ENTRY: /* ignore */ - return; + return 0; case OP_BR: + case OP_SETFVAL: default: break; } insn->bb = NULL; - repeat_phase |= REPEAT_CSE; - return; + return repeat_phase |= REPEAT_CSE; } -/* - * Kill trivially dead instructions - */ +/// +// kill trivially dead instructions static int dead_insn(struct instruction *insn, pseudo_t *src1, pseudo_t *src2, pseudo_t *src3) { - struct pseudo_user *pu; - FOR_EACH_PTR(insn->target->users, pu) { - if (*pu->userp != VOID) - return 0; - } END_FOR_EACH_PTR(pu); + if (has_users(insn->target)) + return 0; insn->bb = NULL; kill_use(src1); @@ -320,11 +402,47 @@ static int dead_insn(struct instruction *insn, pseudo_t *src1, pseudo_t *src2, p return REPEAT_CSE; } +static inline bool has_target(struct instruction *insn) +{ + return opcode_table[insn->opcode].flags & OPF_TARGET; +} + +void remove_dead_insns(struct entrypoint *ep) +{ + struct basic_block *bb; + + FOR_EACH_PTR_REVERSE(ep->bbs, bb) { + struct instruction *insn; + FOR_EACH_PTR_REVERSE(bb->insns, insn) { + if (!insn->bb) + continue; + if (!has_target(insn)) + continue; + if (!has_users(insn->target)) + kill_instruction(insn); + } END_FOR_EACH_PTR_REVERSE(insn); + } END_FOR_EACH_PTR_REVERSE(bb); +} + static inline int constant(pseudo_t pseudo) { return pseudo->type == PSEUDO_VAL; } +/// +// replace the operand of an instruction +// @insn: the instruction +// @pp: the address of the instruction's operand +// @new: the new value for the operand +// @return: REPEAT_CSE. +static inline int replace_pseudo(struct instruction *insn, pseudo_t *pp, pseudo_t new) +{ + pseudo_t old = *pp; + use_pseudo(insn, new, pp); + remove_usage(old, pp); + return REPEAT_CSE; +} + static int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo) { convert_instruction_target(insn, pseudo); @@ -335,13 +453,8 @@ static int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo) kill_use(&insn->src3); case OP_BINARY ... OP_BINCMP_END: kill_use(&insn->src2); - case OP_NOT: - case OP_NEG: + case OP_UNOP ... OP_UNOP_END: case OP_SYMADDR: - case OP_CAST: - case OP_SCAST: - case OP_FPCAST: - case OP_PTRCAST: kill_use(&insn->src1); break; @@ -352,7 +465,14 @@ static int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo) return REPEAT_CSE; } -unsigned int value_size(long long value) +static inline int def_opcode(pseudo_t p) +{ + if (p->type != PSEUDO_REG) + return OP_BADOP; + return p->def->opcode; +} + +static unsigned int value_size(long long value) { value >>= 8; if (!value) @@ -366,19 +486,18 @@ unsigned int value_size(long long value) return 64; } -/* - * Try to determine the maximum size of bits in a pseudo. - * - * Right now this only follow casts and constant values, but we - * could look at things like logical 'and' instructions etc. - */ +/// +// try to determine the maximum size of bits in a pseudo +// +// Right now this only follow casts and constant values, but we +// could look at things like AND instructions, etc. static unsigned int operand_size(struct instruction *insn, pseudo_t pseudo) { unsigned int size = insn->size; if (pseudo->type == PSEUDO_REG) { struct instruction *src = pseudo->def; - if (src && src->opcode == OP_CAST && src->orig_type) { + if (src && src->opcode == OP_ZEXT && src->orig_type) { unsigned int orig_size = src->orig_type->bit_size; if (orig_size < size) size = orig_size; @@ -392,17 +511,417 @@ static unsigned int operand_size(struct instruction *insn, pseudo_t pseudo) return size; } -static int simplify_asr(struct instruction *insn, pseudo_t pseudo, long long value) +static pseudo_t eval_insn(struct instruction *insn) +{ + /* FIXME! Verify signs and sizes!! */ + unsigned int size = insn->size; + long long left = insn->src1->value; + long long right = insn->src2->value; + unsigned long long ul, ur; + long long res, mask, bits; + + mask = 1ULL << (size-1); + bits = mask | (mask-1); + + if (left & mask) + left |= ~bits; + if (right & mask) + right |= ~bits; + ul = left & bits; + ur = right & bits; + + switch (insn->opcode) { + case OP_ADD: + res = left + right; + break; + case OP_SUB: + res = left - right; + break; + case OP_MUL: + res = ul * ur; + break; + case OP_DIVU: + if (!ur) + goto undef; + res = ul / ur; + break; + case OP_DIVS: + if (!right) + goto undef; + if (left == mask && right == -1) + goto undef; + res = left / right; + break; + case OP_MODU: + if (!ur) + goto undef; + res = ul % ur; + break; + case OP_MODS: + if (!right) + goto undef; + if (left == mask && right == -1) + goto undef; + res = left % right; + break; + case OP_SHL: + if (ur >= size) + goto undef; + res = left << right; + break; + case OP_LSR: + if (ur >= size) + goto undef; + res = ul >> ur; + break; + case OP_ASR: + if (ur >= size) + goto undef; + res = left >> right; + break; + /* Logical */ + case OP_AND: + res = left & right; + break; + case OP_OR: + res = left | right; + break; + case OP_XOR: + res = left ^ right; + break; + + /* Binary comparison */ + case OP_SET_EQ: + res = left == right; + break; + case OP_SET_NE: + res = left != right; + break; + case OP_SET_LE: + res = left <= right; + break; + case OP_SET_GE: + res = left >= right; + break; + case OP_SET_LT: + res = left < right; + break; + case OP_SET_GT: + res = left > right; + break; + case OP_SET_B: + res = ul < ur; + break; + case OP_SET_A: + res = ul > ur; + break; + case OP_SET_BE: + res = ul <= ur; + break; + case OP_SET_AE: + res = ul >= ur; + break; + default: + return NULL; + } + res &= bits; + + return value_pseudo(res); + +undef: + return NULL; +} + +/// +// Simplifications +// ^^^^^^^^^^^^^^^ + +/// +// try to simplify MASK(OR(AND(x, M'), b), M) +// @insn: the masking instruction +// @mask: the associated mask (M) +// @ora: one of the OR's operands, guaranteed to be PSEUDO_REG +// @orb: the other OR's operand +// @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise. +static int simplify_mask_or_and(struct instruction *insn, unsigned long long mask, + pseudo_t ora, pseudo_t orb) +{ + unsigned long long omask, nmask; + struct instruction *and = ora->def; + pseudo_t src2 = and->src2; + + if (and->opcode != OP_AND) + return 0; + if (!constant(src2)) + return 0; + omask = src2->value; + nmask = omask & mask; + if (nmask == 0) { + // if (M' & M) == 0: ((a & M') | b) -> b + return replace_pseudo(insn, &insn->src1, orb); + } + if (multi_users(insn->src1)) + return 0; // can't modify anything inside the OR + if (nmask == mask) { + struct instruction *or = insn->src1->def; + pseudo_t *arg = (ora == or->src1) ? &or->src1 : &or->src2; + // if (M' & M) == M: ((a & M') | b) -> (a | b) + return replace_pseudo(or, arg, and->src1); + } + if (nmask != omask && !multi_users(ora)) { + // if (M' & M) != M': AND(a, M') -> AND(a, (M' & M)) + and->src2 = value_pseudo(nmask); + return REPEAT_CSE; + } + return 0; +} + +/// +// try to simplify MASK(OR(a, b), M) +// @insn: the masking instruction +// @mask: the associated mask (M) +// @or: the OR instruction +// @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise. +static int simplify_mask_or(struct instruction *insn, unsigned long long mask, struct instruction *or) +{ + pseudo_t src1 = or->src1; + pseudo_t src2 = or->src2; + int rc; + + if (src1->type == PSEUDO_REG) { + if ((rc = simplify_mask_or_and(insn, mask, src1, src2))) + return rc; + } + if (src2->type == PSEUDO_REG) { + if ((rc = simplify_mask_or_and(insn, mask, src2, src1))) + return rc; + } else if (src2->type == PSEUDO_VAL) { + unsigned long long oval = src2->value; + unsigned long long nval = oval & mask; + // Try to simplify: + // MASK(OR(x, C), M) + if (nval == 0) { + // if (C & M) == 0: OR(x, C) -> x + return replace_pseudo(insn, &insn->src1, src1); + } + if (nval == mask) { + // if (C & M) == M: OR(x, C) -> M + return replace_pseudo(insn, &insn->src1, value_pseudo(mask)); + } + if (nval != oval && !multi_users(or->target)) { + // if (C & M) != C: OR(x, C) -> OR(x, (C & M)) + return replace_pseudo(or, &or->src2, value_pseudo(nval)); + } + } + return 0; +} + +/// +// try to simplify MASK(SHIFT(OR(a, b), S), M) +// @sh: the shift instruction +// @or: the OR instruction +// @mask: the mask associated to MASK (M): +// @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise. +static int simplify_mask_shift_or(struct instruction *sh, struct instruction *or, unsigned long long mask) +{ + unsigned long long smask = bits_mask(sh->size); + int shift = sh->src2->value; + + if (sh->opcode == OP_LSR) + mask <<= shift; + else + mask >>= shift; + return simplify_mask_or(sh, smask & mask, or); +} + +static int simplify_mask_shift(struct instruction *sh, unsigned long long mask) { - unsigned int size = operand_size(insn, pseudo); + struct instruction *inner; - if (value >= size) { - warning(insn->pos, "right shift by bigger than source value"); - return replace_with_pseudo(insn, value_pseudo(insn->type, 0)); + if (!constant(sh->src2) || sh->tainted) + return 0; + switch (DEF_OPCODE(inner, sh->src1)) { + case OP_OR: + if (!multi_users(sh->target)) + return simplify_mask_shift_or(sh, inner, mask); + break; } + return 0; +} + +static long long check_shift_count(struct instruction *insn, unsigned long long uval) +{ + unsigned int size = insn->size; + long long sval = uval; + + if (uval < size) + return uval; + + sval = sign_extend_safe(sval, size); + sval = sign_extend_safe(sval, bits_in_int); + if (sval < 0) + insn->src2 = value_pseudo(sval); + if (insn->tainted) + return sval; + + if (sval < 0 && Wshift_count_negative) + warning(insn->pos, "shift count is negative (%lld)", sval); + if (sval > 0 && Wshift_count_overflow) { + struct symbol *ctype = insn->type; + const char *tname; + if (ctype->type == SYM_NODE) + ctype = ctype->ctype.base_type; + tname = show_typename(ctype); + warning(insn->pos, "shift too big (%llu) for type %s", sval, tname); + } + insn->tainted = 1; + return sval; +} + +static int simplify_shift(struct instruction *insn, pseudo_t pseudo, long long value) +{ + struct instruction *def; + unsigned long long mask, omask, nmask; + unsigned long long nval; + unsigned int size; + pseudo_t src2; + if (!value) return replace_with_pseudo(insn, pseudo); + value = check_shift_count(insn, value); + if (value < 0) + return 0; + + size = insn->size; + switch (insn->opcode) { + case OP_ASR: + if (value >= size) + return 0; + if (pseudo->type != PSEUDO_REG) + break; + def = pseudo->def; + switch (def->opcode) { + case OP_LSR: + case OP_ASR: + if (def == insn) // cyclic DAG! + break; + src2 = def->src2; + if (src2->type != PSEUDO_VAL) + break; + nval = src2->value; + if (nval > insn->size || nval == 0) + break; + value += nval; + if (def->opcode == OP_LSR) + insn->opcode = OP_LSR; + else if (value >= size) + value = size - 1; + goto new_value; + + case OP_ZEXT: + // transform: + // zext.N %t <- (O) %a + // asr.N %r <- %t, C + // into + // zext.N %t <- (O) %a + // lsr.N %r <- %t, C + insn->opcode = OP_LSR; + return REPEAT_CSE; + } + break; + case OP_LSR: + size = operand_size(insn, pseudo); + if (value >= size) + goto zero; + switch(DEF_OPCODE(def, pseudo)) { + case OP_AND: + // replace (A & M) >> S + // by (A >> S) & (M >> S) + if (!constant(def->src2)) + break; + mask = bits_mask(insn->size - value) << value; + omask = def->src2->value; + nmask = omask & mask; + if (nmask == 0) + return replace_with_pseudo(insn, value_pseudo(0)); + if (nmask == mask) + return replace_pseudo(insn, &insn->src1, def->src1); + if (nbr_users(pseudo) > 1) + break; + def->opcode = OP_LSR; + def->src2 = insn->src2; + insn->opcode = OP_AND; + insn->src2 = value_pseudo(omask >> value); + return REPEAT_CSE; + case OP_LSR: + goto case_shift_shift; + case OP_OR: + mask = bits_mask(size); + return simplify_mask_shift_or(insn, def, mask); + case OP_SHL: + // replace ((x << S) >> S) + // by (x & (-1 >> S)) + if (def->src2 != insn->src2) + break; + mask = bits_mask(insn->size - value); + goto replace_mask; + } + break; + case OP_SHL: + if (value >= size) + goto zero; + switch(DEF_OPCODE(def, pseudo)) { + case OP_AND: + // simplify (A & M) << S + if (!constant(def->src2)) + break; + mask = bits_mask(insn->size) >> value; + omask = def->src2->value; + nmask = omask & mask; + if (nmask == 0) + return replace_with_pseudo(insn, value_pseudo(0)); + if (nmask == mask) + return replace_pseudo(insn, &insn->src1, def->src1); + // do not simplify into ((A << S) & (M << S)) + break; + case OP_LSR: + // replace ((x >> S) << S) + // by (x & (-1 << S)) + if (def->src2 != insn->src2) + break; + mask = bits_mask(insn->size - value) << value; + goto replace_mask; + case OP_OR: + mask = bits_mask(size); + return simplify_mask_shift_or(insn, def, mask); + case OP_SHL: + case_shift_shift: // also for LSR - LSR + if (def == insn) // cyclic DAG! + break; + src2 = def->src2; + if (src2->type != PSEUDO_VAL) + break; + nval = src2->value; + if (nval > insn->size) + break; + value += nval; + goto new_value; + } + break; + } return 0; + +new_value: + if (value < size) { + insn->src2 = value_pseudo(value); + return replace_pseudo(insn, &insn->src1, pseudo->def->src1); + } +zero: + return replace_with_pseudo(insn, value_pseudo(0)); +replace_mask: + insn->opcode = OP_AND; + insn->src2 = value_pseudo(mask); + return replace_pseudo(insn, &insn->src1, def->src1); } static int simplify_mul_div(struct instruction *insn, long long value) @@ -414,8 +933,7 @@ static int simplify_mul_div(struct instruction *insn, long long value) return replace_with_pseudo(insn, insn->src1); switch (insn->opcode) { - case OP_MULS: - case OP_MULU: + case OP_MUL: if (value == 0) return replace_with_pseudo(insn, insn->src2); /* Fall through */ @@ -433,48 +951,45 @@ static int simplify_mul_div(struct instruction *insn, long long value) return 0; } -static int compare_opcode(int opcode, int inverse) -{ - if (!inverse) - return opcode; - - switch (opcode) { - case OP_SET_EQ: return OP_SET_NE; - case OP_SET_NE: return OP_SET_EQ; - - case OP_SET_LT: return OP_SET_GE; - case OP_SET_LE: return OP_SET_GT; - case OP_SET_GT: return OP_SET_LE; - case OP_SET_GE: return OP_SET_LT; - - case OP_SET_A: return OP_SET_BE; - case OP_SET_AE: return OP_SET_B; - case OP_SET_B: return OP_SET_AE; - case OP_SET_BE: return OP_SET_A; - - default: - return opcode; - } -} - static int simplify_seteq_setne(struct instruction *insn, long long value) { pseudo_t old = insn->src1; - struct instruction *def = old->def; - pseudo_t src1, src2; + struct instruction *def; + unsigned osize; int inverse; int opcode; if (value != 0 && value != 1) return 0; + if (old->type != PSEUDO_REG) + return 0; + def = old->def; if (!def) return 0; inverse = (insn->opcode == OP_SET_NE) == value; + if (!inverse && def->size == 1 && insn->size == 1) { + // Replace: + // setne %r <- %s, $0 + // or: + // seteq %r <- %s, $1 + // by %s when boolean + return replace_with_pseudo(insn, old); + } opcode = def->opcode; switch (opcode) { - case OP_BINCMP ... OP_BINCMP_END: + case OP_AND: + if (inverse) + break; + if (def->size != insn->size) + break; + if (def->src2->type != PSEUDO_VAL) + break; + if (def->src2->value != 1) + break; + return replace_with_pseudo(insn, old); + case OP_FPCMP ... OP_BINCMP_END: // Convert: // setcc.n %t <- %a, %b // setne.m %r <- %t, $0 @@ -482,64 +997,131 @@ static int simplify_seteq_setne(struct instruction *insn, long long value) // setcc.n %t <- %a, %b // setcc.m %r <- %a, $b // and similar for setne/eq ... 0/1 - src1 = def->src1; - src2 = def->src2; - insn->opcode = compare_opcode(opcode, inverse); - use_pseudo(insn, src1, &insn->src1); - use_pseudo(insn, src2, &insn->src2); + insn->opcode = inverse ? opcode_table[opcode].negate : opcode; + use_pseudo(insn, def->src1, &insn->src1); + use_pseudo(insn, def->src2, &insn->src2); remove_usage(old, &insn->src1); return REPEAT_CSE; - default: - return 0; + case OP_SEXT: + if (value && (def->orig_type->bit_size == 1)) + break; + /* Fall through */ + case OP_ZEXT: + // Convert: + // *ext.m %s <- (1) %a + // setne.1 %r <- %s, $0 + // into: + // setne.1 %s <- %a, $0 + // and same for setne/eq ... 0/1 + return replace_pseudo(insn, &insn->src1, def->src); + case OP_TRUNC: + if (multi_users(old)) + break; + // convert + // trunc.n %s <- (o) %a + // setne.m %r <- %s, $0 + // into: + // and.o %s <- %a, $((1 << o) - 1) + // setne.m %r <- %s, $0 + // and same for setne/eq ... 0/1 + osize = def->size; + def->opcode = OP_AND; + def->type = def->orig_type; + def->size = def->type->bit_size; + def->src2 = value_pseudo(bits_mask(osize)); + return REPEAT_CSE; + } + return 0; +} + +static int simplify_constant_mask(struct instruction *insn, unsigned long long mask) +{ + pseudo_t old = insn->src1; + unsigned long long omask; + unsigned long long nmask; + struct instruction *def; + int osize; + + switch (DEF_OPCODE(def, old)) { + case OP_FPCMP ... OP_BINCMP_END: + osize = 1; + goto oldsize; + case OP_OR: + return simplify_mask_or(insn, mask, def); + case OP_LSR: + case OP_SHL: + return simplify_mask_shift(def, mask); + case OP_ZEXT: + osize = def->orig_type->bit_size; + /* fall through */ + oldsize: + omask = (1ULL << osize) - 1; + nmask = mask & omask; + if (nmask == omask) + // the AND mask is redundant + return replace_with_pseudo(insn, old); + if (nmask != mask) { + // can use a smaller mask + insn->src2 = value_pseudo(nmask); + return REPEAT_CSE; + } + break; } + return 0; } static int simplify_constant_rightside(struct instruction *insn) { long long value = insn->src2->value; + long long sbit = 1ULL << (insn->size - 1); + long long bits = sbit | (sbit - 1); switch (insn->opcode) { - case OP_OR_BOOL: - if (value == 1) + case OP_OR: + if ((value & bits) == bits) return replace_with_pseudo(insn, insn->src2); goto case_neutral_zero; + case OP_XOR: + if ((value & bits) == bits) { + insn->opcode = OP_NOT; + return REPEAT_CSE; + } + goto case_neutral_zero; + case OP_SUB: if (value) { insn->opcode = OP_ADD; - insn->src2 = value_pseudo(insn->type, -value); + insn->src2 = value_pseudo(-value); return REPEAT_CSE; } /* Fall through */ case OP_ADD: - case OP_OR: case OP_XOR: - case OP_SHL: - case OP_LSR: case_neutral_zero: if (!value) return replace_with_pseudo(insn, insn->src1); return 0; case OP_ASR: - return simplify_asr(insn, insn->src1, value); + case OP_SHL: + case OP_LSR: + return simplify_shift(insn, insn->src1, value); case OP_MODU: case OP_MODS: if (value == 1) - return replace_with_pseudo(insn, value_pseudo(insn->type, 0)); + return replace_with_pseudo(insn, value_pseudo(0)); return 0; case OP_DIVU: case OP_DIVS: - case OP_MULU: case OP_MULS: + case OP_MUL: return simplify_mul_div(insn, value); - case OP_AND_BOOL: - if (value == 1) - return replace_with_pseudo(insn, insn->src1); - /* Fall through */ case OP_AND: if (!value) return replace_with_pseudo(insn, insn->src2); - return 0; + if ((value & bits) == bits) + return replace_with_pseudo(insn, insn->src1); + return simplify_constant_mask(insn, value); case OP_SET_NE: case OP_SET_EQ: @@ -561,7 +1143,7 @@ static int simplify_constant_leftside(struct instruction *insn) case OP_SHL: case OP_LSR: case OP_ASR: case OP_AND: - case OP_MULU: case OP_MULS: + case OP_MUL: if (!value) return replace_with_pseudo(insn, insn->src1); return 0; @@ -571,122 +1153,12 @@ static int simplify_constant_leftside(struct instruction *insn) static int simplify_constant_binop(struct instruction *insn) { - /* FIXME! Verify signs and sizes!! */ - long long left = insn->src1->value; - long long right = insn->src2->value; - unsigned long long ul, ur; - long long res, mask, bits; - - mask = 1ULL << (insn->size-1); - bits = mask | (mask-1); - - if (left & mask) - left |= ~bits; - if (right & mask) - right |= ~bits; - ul = left & bits; - ur = right & bits; + pseudo_t res = eval_insn(insn); - switch (insn->opcode) { - case OP_ADD: - res = left + right; - break; - case OP_SUB: - res = left - right; - break; - case OP_MULU: - res = ul * ur; - break; - case OP_MULS: - res = left * right; - break; - case OP_DIVU: - if (!ur) - return 0; - res = ul / ur; - break; - case OP_DIVS: - if (!right) - return 0; - if (left == mask && right == -1) - return 0; - res = left / right; - break; - case OP_MODU: - if (!ur) - return 0; - res = ul % ur; - break; - case OP_MODS: - if (!right) - return 0; - if (left == mask && right == -1) - return 0; - res = left % right; - break; - case OP_SHL: - res = left << right; - break; - case OP_LSR: - res = ul >> ur; - break; - case OP_ASR: - res = left >> right; - break; - /* Logical */ - case OP_AND: - res = left & right; - break; - case OP_OR: - res = left | right; - break; - case OP_XOR: - res = left ^ right; - break; - case OP_AND_BOOL: - res = left && right; - break; - case OP_OR_BOOL: - res = left || right; - break; - - /* Binary comparison */ - case OP_SET_EQ: - res = left == right; - break; - case OP_SET_NE: - res = left != right; - break; - case OP_SET_LE: - res = left <= right; - break; - case OP_SET_GE: - res = left >= right; - break; - case OP_SET_LT: - res = left < right; - break; - case OP_SET_GT: - res = left > right; - break; - case OP_SET_B: - res = ul < ur; - break; - case OP_SET_A: - res = ul > ur; - break; - case OP_SET_BE: - res = ul <= ur; - break; - case OP_SET_AE: - res = ul >= ur; - break; - default: + if (!res) return 0; - } - res &= bits; - replace_with_pseudo(insn, value_pseudo(insn->type, res)); + replace_with_pseudo(insn, res); return REPEAT_CSE; } @@ -700,26 +1172,19 @@ static int simplify_binop_same_args(struct instruction *insn, pseudo_t arg) warning(insn->pos, "self-comparison always evaluates to false"); case OP_SUB: case OP_XOR: - return replace_with_pseudo(insn, value_pseudo(insn->type, 0)); + return replace_with_pseudo(insn, value_pseudo(0)); case OP_SET_EQ: case OP_SET_LE: case OP_SET_GE: case OP_SET_BE: case OP_SET_AE: if (Wtautological_compare) warning(insn->pos, "self-comparison always evaluates to true"); - return replace_with_pseudo(insn, value_pseudo(insn->type, 1)); + return replace_with_pseudo(insn, value_pseudo(1)); case OP_AND: case OP_OR: return replace_with_pseudo(insn, arg); - case OP_AND_BOOL: - case OP_OR_BOOL: - remove_usage(arg, &insn->src2); - insn->src2 = value_pseudo(insn->type, 0); - insn->opcode = OP_SET_NE; - return REPEAT_CSE; - default: break; } @@ -765,13 +1230,23 @@ static int canonical_order(pseudo_t p1, pseudo_t p2) return 1; } -static int simplify_commutative_binop(struct instruction *insn) +static int canonicalize_commutative(struct instruction *insn) { - if (!canonical_order(insn->src1, insn->src2)) { - switch_pseudo(insn, &insn->src1, insn, &insn->src2); - return REPEAT_CSE; - } - return 0; + if (canonical_order(insn->src1, insn->src2)) + return 0; + + switch_pseudo(insn, &insn->src1, insn, &insn->src2); + return repeat_phase |= REPEAT_CSE; +} + +static int canonicalize_compare(struct instruction *insn) +{ + if (canonical_order(insn->src1, insn->src2)) + return 0; + + switch_pseudo(insn, &insn->src1, insn, &insn->src2); + insn->opcode = opcode_table[insn->opcode].swap; + return repeat_phase |= REPEAT_CSE; } static inline int simple_pseudo(pseudo_t pseudo) @@ -795,7 +1270,7 @@ static int simplify_associative_binop(struct instruction *insn) return 0; if (!simple_pseudo(def->src2)) return 0; - if (ptr_list_size((struct ptr_list *)def->target->users) != 1) + if (multi_users(def->target)) return 0; switch_pseudo(def, &def->src1, insn, &insn->src2); return REPEAT_CSE; @@ -813,13 +1288,22 @@ static int simplify_constant_unop(struct instruction *insn) case OP_NEG: res = -val; break; + case OP_SEXT: + mask = 1ULL << (insn->orig_type->bit_size-1); + if (val & mask) + val |= ~(mask | (mask-1)); + /* fall through */ + case OP_ZEXT: + case OP_TRUNC: + res = val; + break; default: return 0; } mask = 1ULL << (insn->size-1); res &= mask | (mask-1); - replace_with_pseudo(insn, value_pseudo(insn->type, res)); + replace_with_pseudo(insn, value_pseudo(res)); return REPEAT_CSE; } @@ -877,7 +1361,7 @@ static int simplify_one_memop(struct instruction *insn, pseudo_t orig) offset: /* Invalid code */ - if (new == orig) { + if (new == orig || new == addr) { if (new == VOID) return 0; /* @@ -889,19 +1373,20 @@ offset: */ if (repeat_phase & REPEAT_CFG_CLEANUP) return 0; - new = VOID; warning(insn->pos, "crazy programmer"); + replace_pseudo(insn, &insn->src, VOID); + return 0; } insn->offset += off->value; - use_pseudo(insn, new, &insn->src); - remove_usage(addr, &insn->src); + replace_pseudo(insn, &insn->src, new); return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP; } -/* - * We walk the whole chain of adds/subs backwards. That's not - * only more efficient, but it allows us to find loops. - */ +/// +// simplify memops instructions +// +// :note: We walk the whole chain of adds/subs backwards. +// That's not only more efficient, but it allows us to find loops. static int simplify_memop(struct instruction *insn) { int one, ret = 0; @@ -914,73 +1399,148 @@ static int simplify_memop(struct instruction *insn) return ret; } -static long long get_cast_value(long long val, int old_size, int new_size, int sign) -{ - long long mask; - - if (sign && new_size > old_size) { - mask = 1 << (old_size-1); - if (val & mask) - val |= ~(mask | (mask-1)); - } - mask = 1 << (new_size-1); - return val & (mask | (mask-1)); -} - static int simplify_cast(struct instruction *insn) { - struct symbol *orig_type; - int orig_size, size; + unsigned long long mask; + struct instruction *def; pseudo_t src; + pseudo_t val; + int osize; + int size; if (dead_insn(insn, &insn->src, NULL, NULL)) return REPEAT_CSE; - orig_type = insn->orig_type; - if (!orig_type) - return 0; - - /* Keep casts with pointer on either side (not only case of OP_PTRCAST) */ - if (is_ptr_type(orig_type) || is_ptr_type(insn->type)) - return 0; - - orig_size = orig_type->bit_size; - size = insn->size; src = insn->src; /* A cast of a constant? */ - if (constant(src)) { - int sign = orig_type->ctype.modifiers & MOD_SIGNED; - long long val = get_cast_value(src->value, orig_size, size, sign); - src = value_pseudo(orig_type, val); - goto simplify; - } + if (constant(src)) + return simplify_constant_unop(insn); - /* A cast of a "and" might be a no-op.. */ - if (src->type == PSEUDO_REG) { - struct instruction *def = src->def; - if (def->opcode == OP_AND && def->size >= size) { - pseudo_t val = def->src2; - if (val->type == PSEUDO_VAL) { - unsigned long long value = val->value; - if (!(value >> (size-1))) - goto simplify; - } - } - } + // can merge with the previous instruction? + size = insn->size; + def = src->def; + switch (def_opcode(src)) { + case OP_AND: + val = def->src2; + if (val->type != PSEUDO_VAL) + break; + /* A cast of a AND might be a no-op.. */ + switch (insn->opcode) { + case OP_TRUNC: + if (multi_users(src)) + break; + def->opcode = OP_TRUNC; + def->orig_type = def->type; + def->type = insn->type; + def->size = size; + + insn->opcode = OP_AND; + mask = val->value; + mask &= (1ULL << size) - 1; + insn->src2 = value_pseudo(mask); + return REPEAT_CSE; - if (size == orig_size) { - int op = (orig_type->ctype.modifiers & MOD_SIGNED) ? OP_SCAST : OP_CAST; - if (insn->opcode == op) - goto simplify; - if (insn->opcode == OP_FPCAST && is_float_type(orig_type)) - goto simplify; + case OP_SEXT: + if (val->value & (1 << (def->size - 1))) + break; + // OK, sign bit is 0 + case OP_ZEXT: + if (multi_users(src)) + break; + // transform: + // and.n %b <- %a, M + // *ext.m %c <- (n) %b + // into: + // zext.m %b <- %a + // and.m %c <- %b, M + // For ZEXT, the mask will always be small + // enough. For SEXT, it can only be done if + // the mask force the sign bit to 0. + def->opcode = OP_ZEXT; + def->orig_type = insn->orig_type; + def->type = insn->type; + def->size = insn->size; + insn->opcode = OP_AND; + insn->src2 = val; + return REPEAT_CSE; + } + break; + case OP_FPCMP ... OP_BINCMP_END: + switch (insn->opcode) { + case OP_SEXT: + if (insn->size == 1) + break; + /* fall through */ + case OP_ZEXT: + case OP_TRUNC: + // simplify: + // setcc.n %t <- %a, %b + // zext.m %r <- (n) %t + // into: + // setcc.m %r <- %a, %b + // and same for s/zext/trunc/ + insn->opcode = def->opcode; + use_pseudo(insn, def->src2, &insn->src2); + return replace_pseudo(insn, &insn->src1, def->src1); + } + break; + case OP_OR: + switch (insn->opcode) { + case OP_TRUNC: + mask = bits_mask(insn->size); + return simplify_mask_or(insn, mask, def); + } + break; + case OP_LSR: + case OP_SHL: + if (insn->opcode != OP_TRUNC) + break; + mask = bits_mask(insn->size); + return simplify_mask_shift(def, mask); + case OP_TRUNC: + switch (insn->opcode) { + case OP_TRUNC: + insn->orig_type = def->orig_type; + return replace_pseudo(insn, &insn->src1, def->src); + case OP_ZEXT: + if (size != def->orig_type->bit_size) + break; + insn->opcode = OP_AND; + insn->src2 = value_pseudo((1ULL << def->size) - 1); + return replace_pseudo(insn, &insn->src1, def->src); + } + break; + case OP_ZEXT: + switch (insn->opcode) { + case OP_SEXT: + insn->opcode = OP_ZEXT; + /* fall through */ + case OP_ZEXT: + insn->orig_type = def->orig_type; + return replace_pseudo(insn, &insn->src, def->src); + } + /* fall through */ + case OP_SEXT: + switch (insn->opcode) { + case OP_TRUNC: + osize = def->orig_type->bit_size; + if (size == osize) + return replace_with_pseudo(insn, def->src); + if (size > osize) + insn->opcode = def->opcode; + insn->orig_type = def->orig_type; + return replace_pseudo(insn, &insn->src, def->src); + } + switch (insn->opcode) { + case OP_SEXT: + insn->orig_type = def->orig_type; + return replace_pseudo(insn, &insn->src, def->src); + } + break; } return 0; - -simplify: - return replace_with_pseudo(insn, src); } static int simplify_select(struct instruction *insn) @@ -1019,6 +1579,12 @@ static int simplify_select(struct instruction *insn) return REPEAT_CSE; } } + if (cond == src2 && is_zero(src1)) { + kill_use(&insn->src1); + kill_use(&insn->src3); + replace_with_pseudo(insn, value_pseudo(0)); + return REPEAT_CSE; + } return 0; } @@ -1051,18 +1617,15 @@ static int simplify_range(struct instruction *insn) return 0; } -/* - * Simplify "set_ne/eq $0 + br" - */ -static int simplify_cond_branch(struct instruction *br, pseudo_t cond, struct instruction *def, pseudo_t *pp) +/// +// simplify SET_NE/EQ $0 + BR +static int simplify_cond_branch(struct instruction *br, struct instruction *def, pseudo_t newcond) { - use_pseudo(br, *pp, &br->cond); - remove_usage(cond, &br->cond); + replace_pseudo(br, &br->cond, newcond); if (def->opcode == OP_SET_EQ) { - struct basic_block *true = br->bb_true; - struct basic_block *false = br->bb_false; - br->bb_false = true; - br->bb_true = false; + struct basic_block *tmp = br->bb_true; + br->bb_true = br->bb_false; + br->bb_false = tmp; } return REPEAT_CSE; } @@ -1096,9 +1659,9 @@ static int simplify_branch(struct instruction *insn) if (def->opcode == OP_SET_NE || def->opcode == OP_SET_EQ) { if (constant(def->src1) && !def->src1->value) - return simplify_cond_branch(insn, cond, def, &def->src2); + return simplify_cond_branch(insn, def, def->src2); if (constant(def->src2) && !def->src2->value) - return simplify_cond_branch(insn, cond, def, &def->src1); + return simplify_cond_branch(insn, def, def->src1); } if (def->opcode == OP_SEL) { if (constant(def->src2) && constant(def->src3)) { @@ -1113,24 +1676,15 @@ static int simplify_branch(struct instruction *insn) return REPEAT_CSE; } if (val2) { - struct basic_block *true = insn->bb_true; - struct basic_block *false = insn->bb_false; - insn->bb_false = true; - insn->bb_true = false; + struct basic_block *tmp = insn->bb_true; + insn->bb_true = insn->bb_false; + insn->bb_false = tmp; } - use_pseudo(insn, def->src1, &insn->cond); - remove_usage(cond, &insn->cond); - return REPEAT_CSE; - } - } - if (def->opcode == OP_CAST || def->opcode == OP_SCAST) { - int orig_size = def->orig_type ? def->orig_type->bit_size : 0; - if (def->size > orig_size) { - use_pseudo(insn, def->src, &insn->cond); - remove_usage(cond, &insn->cond); - return REPEAT_CSE; + return replace_pseudo(insn, &insn->cond, def->src1); } } + if (def->opcode == OP_SEXT || def->opcode == OP_ZEXT) + return replace_pseudo(insn, &insn->cond, def->src); } return 0; } @@ -1165,45 +1719,64 @@ int simplify_instruction(struct instruction *insn) if (!insn->bb) return 0; switch (insn->opcode) { - case OP_ADD: case OP_MULS: + case OP_ADD: case OP_MUL: case OP_AND: case OP_OR: case OP_XOR: - case OP_AND_BOOL: case OP_OR_BOOL: + canonicalize_commutative(insn); if (simplify_binop(insn)) return REPEAT_CSE; - if (simplify_commutative_binop(insn)) - return REPEAT_CSE; return simplify_associative_binop(insn); - case OP_MULU: case OP_SET_EQ: case OP_SET_NE: - if (simplify_binop(insn)) - return REPEAT_CSE; - return simplify_commutative_binop(insn); + canonicalize_commutative(insn); + return simplify_binop(insn); + case OP_SET_LE: case OP_SET_GE: + case OP_SET_LT: case OP_SET_GT: + case OP_SET_B: case OP_SET_A: + case OP_SET_BE: case OP_SET_AE: + canonicalize_compare(insn); + /* fall through */ case OP_SUB: case OP_DIVU: case OP_DIVS: case OP_MODU: case OP_MODS: case OP_SHL: case OP_LSR: case OP_ASR: - case OP_SET_LE: case OP_SET_GE: - case OP_SET_LT: case OP_SET_GT: - case OP_SET_B: case OP_SET_A: - case OP_SET_BE: case OP_SET_AE: return simplify_binop(insn); - case OP_NOT: case OP_NEG: + case OP_NOT: case OP_NEG: case OP_FNEG: return simplify_unop(insn); - case OP_LOAD: case OP_STORE: + case OP_LOAD: + if (!has_users(insn->target)) + return kill_instruction(insn); + /* fall-through */ + case OP_STORE: return simplify_memop(insn); case OP_SYMADDR: - if (dead_insn(insn, NULL, NULL, NULL)) + if (dead_insn(insn, &insn->src, NULL, NULL)) return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP; - return replace_with_pseudo(insn, insn->symbol); - case OP_CAST: - case OP_SCAST: - case OP_FPCAST: - case OP_PTRCAST: + return replace_with_pseudo(insn, insn->src); + case OP_SEXT: case OP_ZEXT: + case OP_TRUNC: return simplify_cast(insn); + case OP_FCVTU: case OP_FCVTS: + case OP_UCVTF: case OP_SCVTF: + case OP_FCVTF: + case OP_PTRCAST: + if (dead_insn(insn, &insn->src, NULL, NULL)) + return REPEAT_CSE; + break; + case OP_UTPTR: + case OP_PTRTU: + return replace_with_pseudo(insn, insn->src); + case OP_SLICE: + if (dead_insn(insn, &insn->src, NULL, NULL)) + return REPEAT_CSE; + break; + case OP_SETVAL: + case OP_SETFVAL: + if (dead_insn(insn, NULL, NULL, NULL)) + return REPEAT_CSE; + break; case OP_PHI: if (dead_insn(insn, NULL, NULL, NULL)) { kill_use_list(insn->phi_list); @@ -1222,6 +1795,13 @@ int simplify_instruction(struct instruction *insn) return simplify_switch(insn); case OP_RANGE: return simplify_range(insn); + case OP_FADD: + case OP_FSUB: + case OP_FMUL: + case OP_FDIV: + if (dead_insn(insn, &insn->src1, &insn->src2, NULL)) + return REPEAT_CSE; + break; } return 0; } diff --git a/usr/src/tools/smatch/src/smatch.h b/usr/src/tools/smatch/src/smatch.h index 3b94e6577d..a7ca2524a6 100644 --- a/usr/src/tools/smatch/src/smatch.h +++ b/usr/src/tools/smatch/src/smatch.h @@ -107,6 +107,7 @@ struct bit_info { enum hook_type { EXPR_HOOK, + EXPR_HOOK_AFTER, STMT_HOOK, STMT_HOOK_AFTER, SYM_HOOK, @@ -156,7 +157,7 @@ typedef struct smatch_state *(merge_func_t)(struct smatch_state *s1, struct smat typedef struct smatch_state *(unmatched_func_t)(struct sm_state *state); void add_merge_hook(int client_id, merge_func_t *func); void add_unmatched_state_hook(int client_id, unmatched_func_t *func); -void add_pre_merge_hook(int client_id, void (*hook)(struct sm_state *sm)); +void add_pre_merge_hook(int client_id, void (*hook)(struct sm_state *cur, struct sm_state *other)); typedef void (scope_hook)(void *data); void add_scope_hook(scope_hook *hook, void *data); typedef void (func_hook)(const char *fn, struct expression *expr, void *data); @@ -205,6 +206,7 @@ extern int final_pass; extern struct symbol *cur_func_sym; extern int option_debug; extern int local_debug; +bool debug_implied(void); extern int option_info; extern int option_spammy; extern int option_timeout; @@ -367,6 +369,7 @@ void add_get_state_hook(void (*fn)(int owner, const char *name, struct symbol *s /* smatch_helper.c */ DECLARE_PTR_LIST(int_stack, int); char *alloc_string(const char *str); +char *alloc_string_newline(const char *str); void free_string(char *str); void append(char *dest, const char *data, int buff_len); void remove_parens(char *str); @@ -401,15 +404,18 @@ int get_absolute_min(struct expression *expr, sval_t *sval); int get_absolute_max(struct expression *expr, sval_t *sval); int parse_call_math(struct expression *expr, char *math, sval_t *val); int parse_call_math_rl(struct expression *call, const char *math, struct range_list **rl); +const char *get_allocation_math(struct expression *expr); char *get_value_in_terms_of_parameter_math(struct expression *expr); char *get_value_in_terms_of_parameter_math_var_sym(const char *var, struct symbol *sym); -int is_zero(struct expression *expr); +int expr_is_zero(struct expression *expr); int known_condition_true(struct expression *expr); int known_condition_false(struct expression *expr); int implied_condition_true(struct expression *expr); int implied_condition_false(struct expression *expr); int can_integer_overflow(struct symbol *type, struct expression *expr); void clear_math_cache(void); +void set_fast_math_only(void); +void clear_fast_math_only(void); int is_array(struct expression *expr); struct expression *get_array_base(struct expression *expr); @@ -421,7 +427,7 @@ struct expression *strip_expr(struct expression *expr); struct expression *strip_expr_set_parent(struct expression *expr); void scoped_state(int my_id, const char *name, struct symbol *sym); int is_error_return(struct expression *expr); -int getting_address(void); +int getting_address(struct expression *expr); int get_struct_and_member(struct expression *expr, const char **type, const char **member); char *get_member_name(struct expression *expr); char *get_fnptr_name(struct expression *expr); @@ -462,7 +468,7 @@ int is_void_pointer(struct expression *expr); int is_char_pointer(struct expression *expr); int is_string(struct expression *expr); int is_static(struct expression *expr); -int is_local_variable(struct expression *expr); +bool is_local_variable(struct expression *expr); int types_equiv(struct symbol *one, struct symbol *two); int fn_static(void); const char *global_static(); @@ -524,6 +530,7 @@ void __split_expr(struct expression *expr); void __split_label_stmt(struct statement *stmt); void __split_stmt(struct statement *stmt); extern int __in_function_def; +extern int __in_unmatched_hook; extern int option_assume_loops; extern int option_two_passes; extern int option_no_db; @@ -673,11 +680,6 @@ char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct sy int get_absolute_min_helper(struct expression *expr, sval_t *sval); int get_absolute_max_helper(struct expression *expr, sval_t *sval); -/* smatch_local_values.c */ -int get_local_rl(struct expression *expr, struct range_list **rl); -int get_local_max_helper(struct expression *expr, sval_t *sval); -int get_local_min_helper(struct expression *expr, sval_t *sval); - /* smatch_type_value.c */ int get_db_type_rl(struct expression *expr, struct range_list **rl); /* smatch_data_val.c */ @@ -686,7 +688,7 @@ int get_mtag_rl(struct expression *expr, struct range_list **rl); int get_array_rl(struct expression *expr, struct range_list **rl); /* smatch_states.c */ -void __swap_cur_stree(struct stree *stree); +struct stree *__swap_cur_stree(struct stree *stree); void __push_fake_cur_stree(); struct stree *__pop_fake_cur_stree(); void __free_fake_cur_stree(); @@ -695,6 +697,8 @@ void __pop_fake_cur_stree_fast(void); void __merge_stree_into_cur(struct stree *stree); int unreachable(void); +void __set_cur_stree_readonly(void); +void __set_cur_stree_writable(void); void __set_sm(struct sm_state *sm); void __set_sm_cur_stree(struct sm_state *sm); void __set_sm_fake_stree(struct sm_state *sm); @@ -768,7 +772,6 @@ void __print_cur_stree(void); /* smatch_hooks.c */ void __pass_to_client(void *data, enum hook_type type); -void __pass_to_client_no_data(enum hook_type type); void __pass_case_to_client(struct expression *switch_expr, struct range_list *rl); int __has_merge_function(int client_id); @@ -776,7 +779,7 @@ struct smatch_state *__client_merge_function(int owner, struct smatch_state *s1, struct smatch_state *s2); struct smatch_state *__client_unmatched_state_function(struct sm_state *sm); -void call_pre_merge_hook(struct sm_state *sm); +void call_pre_merge_hook(struct sm_state *cur, struct sm_state *other); void __push_scope_hooks(void); void __call_scope_hooks(void); @@ -874,6 +877,7 @@ void select_call_implies_hook(int type, void (*callback)(struct expression *call void select_return_implies_hook(int type, void (*callback)(struct expression *call, struct expression *arg, char *key, char *value)); struct range_list *db_return_vals(struct expression *expr); struct range_list *db_return_vals_from_str(const char *fn_name); +struct range_list *db_return_vals_no_args(struct expression *expr); char *return_state_to_var_sym(struct expression *expr, int param, const char *key, struct symbol **sym); char *get_chunk_from_key(struct expression *arg, char *key, struct symbol **sym, struct var_sym_list **vsl); char *get_variable_from_key(struct expression *arg, const char *key, struct symbol **sym); @@ -1058,6 +1062,8 @@ void __add_return_to_param_mapping(struct expression *assign, const char *return char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym); /* smatch_comparison.c */ +#define UNKNOWN_COMPARISON 0 +#define IMPOSSIBLE_COMPARISON -1 struct compare_data { /* The ->left and ->right expression pointers might be NULL (I'm lazy) */ struct expression *left; @@ -1075,7 +1081,7 @@ struct smatch_state *alloc_compare_state( int comparison, struct expression *right, const char *right_var, struct var_sym_list *right_vsl); -int filter_comparison(int orig, int op); +int comparison_intersection(int orig, int op); int merge_comparisons(int one, int two); int combine_comparisons(int left_compare, int right_compare); int state_to_comparison(struct smatch_state *state); @@ -1173,6 +1179,7 @@ int get_formatted_string_min_size(struct expression *call, int arg); /* smatch_param_set.c */ int param_was_set(struct expression *expr); int param_was_set_var_sym(const char *name, struct symbol *sym); +void print_limited_param_set(int return_id, char *return_ranges, struct expression *expr); /* smatch_param_filter.c */ int param_has_filter_data(struct sm_state *sm); @@ -1181,9 +1188,6 @@ void set_up_link_functions(int id, int linkid); struct smatch_state *merge_link_states(struct smatch_state *s1, struct smatch_state *s2); void store_link(int link_id, const char *name, struct symbol *sym, const char *link_name, struct symbol *link_sym); -/* smatch_auto_copy.c */ -void set_auto_copy(int owner); - /* check_buf_comparison */ const char *limit_type_str(unsigned int limit_type); struct expression *get_size_variable(struct expression *buf, int *limit_type); @@ -1235,13 +1239,14 @@ int get_string_mtag(struct expression *expr, mtag_t *tag); int get_toplevel_mtag(struct symbol *sym, mtag_t *tag); int create_mtag_alias(mtag_t tag, struct expression *expr, mtag_t *new); int expr_to_mtag_offset(struct expression *expr, mtag_t *tag, int *offset); -void update_mtag_data(struct expression *expr); +void update_mtag_data(struct expression *expr, struct smatch_state *state); int get_mtag_sval(struct expression *expr, sval_t *sval); /* Trinity fuzzer stuff */ const char *get_syscall_arg_type(struct symbol *sym); /* smatch_bit_info.c */ +struct bit_info *rl_to_binfo(struct range_list *rl); struct bit_info *get_bit_info(struct expression *expr); struct bit_info *get_bit_info_var_sym(const char *name, struct symbol *sym); /* smatch_mem_tracker.c */ @@ -1254,6 +1259,7 @@ bool is_nospec(struct expression *expr); long get_stmt_cnt(void); /* smatch_nul_terminator.c */ +bool is_nul_terminated_var_sym(const char *name, struct symbol *sym); bool is_nul_terminated(struct expression *expr); /* check_kernel.c */ bool is_ignored_kernel_data(const char *name); diff --git a/usr/src/tools/smatch/src/smatch_array_values.c b/usr/src/tools/smatch/src/smatch_array_values.c index 3f9f73383e..82da1d858b 100644 --- a/usr/src/tools/smatch/src/smatch_array_values.c +++ b/usr/src/tools/smatch/src/smatch_array_values.c @@ -188,10 +188,37 @@ static void match_assign(struct expression *expr) update_cache(name, is_file_local(array), rl); } +static void mark_strings_unknown(const char *fn, struct expression *expr, void *_arg) +{ + struct expression *dest; + struct symbol *type; + int arg = PTR_INT(_arg); + char *name; + + dest = get_argument_from_call_expr(expr->args, arg); + if (!dest) + return; + name = get_array_name(dest); + if (!name) + return; + type = get_type(dest); + if (type_is_ptr(type)) + type = get_real_base_type(type); + update_cache(name, is_file_local(dest), alloc_whole_rl(type)); +} + void register_array_values(int id) { my_id = id; add_hook(&match_assign, ASSIGNMENT_HOOK); add_hook(&match_assign, GLOBAL_ASSIGNMENT_HOOK); + + add_function_hook("sprintf", &mark_strings_unknown, INT_PTR(0)); + add_function_hook("snprintf", &mark_strings_unknown, INT_PTR(0)); + + add_function_hook("strcpy", &mark_strings_unknown, INT_PTR(0)); + add_function_hook("strncpy", &mark_strings_unknown, INT_PTR(0)); + add_function_hook("strlcpy", &mark_strings_unknown, INT_PTR(0)); + add_function_hook("strscpy", &mark_strings_unknown, INT_PTR(0)); } diff --git a/usr/src/tools/smatch/src/smatch_assigned_expr.c b/usr/src/tools/smatch/src/smatch_assigned_expr.c index 188577e42c..e23e1104b0 100644 --- a/usr/src/tools/smatch/src/smatch_assigned_expr.c +++ b/usr/src/tools/smatch/src/smatch_assigned_expr.c @@ -51,7 +51,7 @@ struct expression *get_assigned_expr_name_sym(const char *name, struct symbol *s { struct smatch_state *state; - state = get_state(my_id, name, sym); + state = __get_state(my_id, name, sym); if (!state) return NULL; return (struct expression *)state->data; diff --git a/usr/src/tools/smatch/src/smatch_auto_copy.c b/usr/src/tools/smatch/src/smatch_auto_copy.c deleted file mode 100644 index 098404e66d..0000000000 --- a/usr/src/tools/smatch/src/smatch_auto_copy.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2014 Oracle. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt - */ - -#include "smatch.h" -#include "smatch_slist.h" - -static int my_id; - -static int *auto_copy; - -void set_auto_copy(int owner) -{ - if (owner <= 1 || owner > num_checks) { - sm_ierror("bogus set_auto_copy()"); - return; - } - auto_copy[owner] = 1; -} - -static void match_assign(struct expression *expr) -{ - char *left_name = NULL; - char *right_name = NULL; - struct symbol *left_sym, *right_sym; - struct state_list *slist = NULL; - struct sm_state *sm; - - left_name = expr_to_var_sym(expr->left, &left_sym); - if (!left_name || !left_sym) - goto free; - right_name = expr_to_var_sym(expr->right, &right_sym); - if (!right_name || !right_sym) - goto free; - - FOR_EACH_SM(__get_cur_stree(), sm) { - if (sm->owner <= 1 || sm->owner > num_checks) - continue; - if (!auto_copy[sm->owner]) - continue; - if (right_sym != sm->sym) - continue; - if (strcmp(right_name, sm->name) != 0) - continue; - add_ptr_list(&slist, sm); - } END_FOR_EACH_SM(sm); - - - FOR_EACH_PTR(slist, sm) { - set_state(sm->owner, left_name, left_sym, sm->state); - } END_FOR_EACH_PTR(sm); - -free: - free_slist(&slist); - free_string(left_name); - free_string(right_name); -} - -void register_auto_copy(int id) -{ - my_id = id; - auto_copy = malloc((num_checks + 1) * sizeof(*auto_copy)); - memset(auto_copy, 0, (num_checks + 1) * sizeof(*auto_copy)); - - add_hook(&match_assign, ASSIGNMENT_HOOK); -} diff --git a/usr/src/tools/smatch/src/smatch_bits.c b/usr/src/tools/smatch/src/smatch_bits.c index 0492203836..2fcd5e8f60 100644 --- a/usr/src/tools/smatch/src/smatch_bits.c +++ b/usr/src/tools/smatch/src/smatch_bits.c @@ -54,7 +54,7 @@ static struct smatch_state *alloc_bstate(unsigned long long set, unsigned long l return state; } -static struct bit_info *rl_to_binfo(struct range_list *rl) +struct bit_info *rl_to_binfo(struct range_list *rl) { struct bit_info *ret = __alloc_bit_info(0); sval_t sval; diff --git a/usr/src/tools/smatch/src/smatch_buf_size.c b/usr/src/tools/smatch/src/smatch_buf_size.c index b7bad47254..bd10f7e505 100644 --- a/usr/src/tools/smatch/src/smatch_buf_size.c +++ b/usr/src/tools/smatch/src/smatch_buf_size.c @@ -23,7 +23,7 @@ #include "smatch_extra.h" #include "smatch_function_hashtable.h" -#define UNKNOWN_SIZE (-1) +#define UNKNOWN_SIZE -1 static int my_size_id; @@ -274,8 +274,7 @@ static void db_returns_buf_size(struct expression *expr, int param, char *unused return; call = strip_expr(expr->right); - if (!parse_call_math_rl(call, math, &rl)) - return; + call_results_to_rl(call, &int_ctype, math, &rl); rl = cast_rl(&int_ctype, rl); set_state_expr(my_size_id, expr->left, alloc_estate_rl(rl)); } @@ -471,6 +470,7 @@ static struct range_list *alloc_int_rl(int value) struct range_list *get_array_size_bytes_rl(struct expression *expr) { struct range_list *ret = NULL; + sval_t sval; int size; expr = remove_addr_fluff(expr); @@ -528,6 +528,8 @@ struct range_list *get_array_size_bytes_rl(struct expression *expr) return alloc_int_rl(size); ret = size_from_db(expr); + if (rl_to_sval(ret, &sval) && sval.value == -1) + return NULL; if (ret) return ret; @@ -632,6 +634,8 @@ static void store_alloc(struct expression *expr, struct range_list *rl) struct symbol *type; rl = clone_rl(rl); // FIXME!!! + if (!rl) + rl = size_to_rl(UNKNOWN_SIZE); set_state_expr(my_size_id, expr, alloc_estate_rl(rl)); type = get_type(expr); @@ -719,7 +723,7 @@ static void match_calloc(const char *fn, struct expression *expr, void *unused) if (get_implied_rl(mult, &rl)) store_alloc(expr->left, rl); else - store_alloc(expr->left, size_to_rl(-1)); + store_alloc(expr->left, size_to_rl(UNKNOWN_SIZE)); } static void match_page(const char *fn, struct expression *expr, void *_unused) @@ -744,7 +748,7 @@ static void match_strndup(const char *fn, struct expression *expr, void *unused) size.value++; store_alloc(expr->left, size_to_rl(size.value)); } else { - store_alloc(expr->left, size_to_rl(-1)); + store_alloc(expr->left, size_to_rl(UNKNOWN_SIZE)); } } @@ -818,11 +822,13 @@ static void match_call(struct expression *expr) static void struct_member_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm) { - if (sm->state == &merged || - strcmp(sm->state->name, "(-1)") == 0 || - strcmp(sm->state->name, "empty") == 0 || - strcmp(sm->state->name, "0") == 0) + sval_t sval; + + if (!estate_rl(sm->state) || + (estate_get_single_value(sm->state, &sval) && + (sval.value == -1 || sval.value == 0))) return; + sql_insert_caller_info(call, BUF_SIZE, param, printed_name, sm->state->name); } @@ -834,14 +840,28 @@ static void struct_member_callback(struct expression *call, int param, char *pri */ static void print_returned_allocations(int return_id, char *return_ranges, struct expression *expr) { - char buf[16]; - int size; + const char *param_math; + struct range_list *rl; + char buf[64]; + sval_t sval; + + rl = get_array_size_bytes_rl(expr); + param_math = get_allocation_math(expr); + if (!rl && !param_math) + return; - size = get_array_size_bytes(expr); - if (!size) + if (!param_math && + rl_to_sval(rl, &sval) && + (sval.value == -1 || sval.value == 0)) return; - snprintf(buf, sizeof(buf), "%d", size); + if (param_math) + snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), param_math); + else + snprintf(buf, sizeof(buf), "%s", show_rl(rl)); + + // FIXME: don't store if you can guess the size from the type + // FIXME: return if we allocate a parameter $0->bar sql_insert_return_states(return_id, return_ranges, BUF_SIZE, -1, "", buf); } @@ -872,6 +892,7 @@ void register_buf_size(int id) set_dynamic_states(my_size_id); add_unmatched_state_hook(my_size_id, &unmatched_size_state); + add_merge_hook(my_size_id, &merge_estates); select_caller_info_hook(set_param_buf_size, BUF_SIZE); select_return_states_hook(BUF_SIZE, &db_returns_buf_size); @@ -905,13 +926,14 @@ void register_buf_size(int id) add_allocation_function("__alloc_bootmem", &match_alloc, 0); add_allocation_function("alloc_bootmem", &match_alloc, 0); add_allocation_function("kmap", &match_page, 0); + add_allocation_function("kmap_atomic", &match_page, 0); add_allocation_function("get_zeroed_page", &match_page, 0); add_allocation_function("alloc_page", &match_page, 0); - add_allocation_function("page_address", &match_page, 0); - add_allocation_function("lowmem_page_address", &match_page, 0); add_allocation_function("alloc_pages", &match_alloc_pages, 1); add_allocation_function("alloc_pages_current", &match_alloc_pages, 1); add_allocation_function("__get_free_pages", &match_alloc_pages, 1); + add_allocation_function("dma_alloc_contiguous", &match_alloc, 1); + add_allocation_function("dma_alloc_coherent", &match_alloc, 1); } add_allocation_function("strndup", match_strndup, 0); diff --git a/usr/src/tools/smatch/src/smatch_common_functions.c b/usr/src/tools/smatch/src/smatch_common_functions.c index 7c3446ec97..795f0f339e 100644 --- a/usr/src/tools/smatch/src/smatch_common_functions.c +++ b/usr/src/tools/smatch/src/smatch_common_functions.c @@ -69,16 +69,15 @@ static int match_strnlen(struct expression *call, void *unused, struct range_lis static int match_sprintf(struct expression *call, void *_arg, struct range_list **rl) { int str_arg = PTR_INT(_arg); - int size; + int min, max; - size = get_formatted_string_size(call, str_arg); - if (size <= 0) { + min = get_formatted_string_min_size(call, str_arg); + max = get_formatted_string_size(call, str_arg); + if (min < 0 || max < 0) { *rl = alloc_whole_rl(&ulong_ctype); } else { - /* FIXME: This is bogus. get_formatted_string_size() should be - returning a range_list. Also it should not add the NUL. */ - size--; - *rl = alloc_rl(ll_to_sval(0), ll_to_sval(size)); + *rl = alloc_rl(ll_to_sval(min), ll_to_sval(max)); + *rl = cast_rl(get_type(call), *rl); } return 1; } diff --git a/usr/src/tools/smatch/src/smatch_comparison.c b/usr/src/tools/smatch/src/smatch_comparison.c index 44e6717b2c..cd31f09b1b 100644 --- a/usr/src/tools/smatch/src/smatch_comparison.c +++ b/usr/src/tools/smatch/src/smatch_comparison.c @@ -57,6 +57,15 @@ static struct symbol *vsl_to_sym(struct var_sym_list *vsl) return vs->sym; } +static const char *show_comparison(int comparison) +{ + if (comparison == IMPOSSIBLE_COMPARISON) + return "impossible"; + if (comparison == UNKNOWN_COMPARISON) + return "unknown"; + return show_special(comparison); +} + struct smatch_state *alloc_compare_state( struct expression *left, const char *left_var, struct var_sym_list *left_vsl, @@ -68,7 +77,7 @@ struct smatch_state *alloc_compare_state( struct compare_data *data; state = __alloc_smatch_state(0); - state->name = alloc_sname(show_special(comparison)); + state->name = alloc_sname(show_comparison(comparison)); data = __alloc_compare_data(0); data->left = left; data->left_var = alloc_sname(left_var); @@ -84,7 +93,7 @@ struct smatch_state *alloc_compare_state( int state_to_comparison(struct smatch_state *state) { if (!state || !state->data) - return 0; + return UNKNOWN_COMPARISON; return ((struct compare_data *)state->data)->comparison; } @@ -94,8 +103,8 @@ int state_to_comparison(struct smatch_state *state) int flip_comparison(int op) { switch (op) { - case 0: - return 0; + case UNKNOWN_COMPARISON: + return UNKNOWN_COMPARISON; case '<': return '>'; case SPECIAL_UNSIGNED_LT: @@ -116,6 +125,8 @@ int flip_comparison(int op) return '<'; case SPECIAL_UNSIGNED_GT: return SPECIAL_UNSIGNED_LT; + case IMPOSSIBLE_COMPARISON: + return UNKNOWN_COMPARISON; default: sm_perror("unhandled comparison %d", op); return op; @@ -125,8 +136,8 @@ int flip_comparison(int op) int negate_comparison(int op) { switch (op) { - case 0: - return 0; + case UNKNOWN_COMPARISON: + return UNKNOWN_COMPARISON; case '<': return SPECIAL_GTE; case SPECIAL_UNSIGNED_LT: @@ -147,6 +158,8 @@ int negate_comparison(int op) return SPECIAL_LTE; case SPECIAL_UNSIGNED_GT: return SPECIAL_UNSIGNED_LTE; + case IMPOSSIBLE_COMPARISON: + return UNKNOWN_COMPARISON; default: sm_perror("unhandled comparison %d", op); return op; @@ -159,7 +172,7 @@ static int rl_comparison(struct range_list *left_rl, struct range_list *right_rl struct symbol *type = &int_ctype; if (!left_rl || !right_rl) - return 0; + return UNKNOWN_COMPARISON; if (type_positive_bits(rl_type(left_rl)) > type_positive_bits(type)) type = rl_type(left_rl); @@ -188,7 +201,7 @@ static int rl_comparison(struct range_list *left_rl, struct range_list *right_rl if (sval_cmp(left_min, right_max) == 0) return SPECIAL_GTE; - return 0; + return UNKNOWN_COMPARISON; } static int comparison_from_extra(struct expression *a, struct expression *b) @@ -196,9 +209,9 @@ static int comparison_from_extra(struct expression *a, struct expression *b) struct range_list *left, *right; if (!get_implied_rl(a, &left)) - return 0; + return UNKNOWN_COMPARISON; if (!get_implied_rl(b, &right)) - return 0; + return UNKNOWN_COMPARISON; return rl_comparison(left, right); } @@ -221,29 +234,32 @@ static struct smatch_state *unmatched_comparison(struct sm_state *sm) { struct compare_data *data = sm->state->data; struct range_list *left_rl, *right_rl; - int op; + int op = UNKNOWN_COMPARISON; if (!data) return &undefined; + if (is_impossible_path()) { + op = IMPOSSIBLE_COMPARISON; + goto alloc; + } + if (strstr(data->left_var, " orig")) left_rl = get_orig_rl(data->left_vsl); else if (!get_implied_rl_var_sym(data->left_var, vsl_to_sym(data->left_vsl), &left_rl)) - return &undefined; + goto alloc; if (strstr(data->right_var, " orig")) right_rl = get_orig_rl(data->right_vsl); else if (!get_implied_rl_var_sym(data->right_var, vsl_to_sym(data->right_vsl), &right_rl)) - return &undefined; + goto alloc; op = rl_comparison(left_rl, right_rl); - if (op) - return alloc_compare_state( - data->left, data->left_var, data->left_vsl, - op, - data->right, data->right_var, data->right_vsl); - return &undefined; +alloc: + return alloc_compare_state(data->left, data->left_var, data->left_vsl, + op, + data->right, data->right_var, data->right_vsl); } /* remove_unsigned_from_comparison() is obviously a hack. */ @@ -271,8 +287,13 @@ int merge_comparisons(int one, int two) { int LT, EQ, GT; - if (!one || !two) - return 0; + if (one == UNKNOWN_COMPARISON || two == UNKNOWN_COMPARISON) + return UNKNOWN_COMPARISON; + + if (one == IMPOSSIBLE_COMPARISON) + return two; + if (two == IMPOSSIBLE_COMPARISON) + return one; one = remove_unsigned_from_comparison(one); two = remove_unsigned_from_comparison(two); @@ -321,7 +342,7 @@ int merge_comparisons(int one, int two) } if (LT && EQ && GT) - return 0; + return UNKNOWN_COMPARISON; if (LT && EQ) return SPECIAL_LTE; if (LT && GT) @@ -332,17 +353,13 @@ int merge_comparisons(int one, int two) return SPECIAL_GTE; if (GT) return '>'; - return 0; + return UNKNOWN_COMPARISON; } /* - * This is for if you have "a < b" and "b <= c". Or in other words, - * "a < b <= c". You would call this like get_combined_comparison('<', '<='). + * This is for if you have "a < b" and "b <= c" and you want to see how "a + * compares to c". You would call this like get_combined_comparison('<', '<='). * The return comparison would be '<'. - * - * This function is different from merge_comparisons(), for example: - * merge_comparison('<', '==') returns '<=' - * get_combined_comparison('<', '==') returns '<' */ int combine_comparisons(int left_compare, int right_compare) { @@ -400,144 +417,148 @@ int combine_comparisons(int left_compare, int right_compare) return SPECIAL_GTE; return '>'; } - return 0; + return UNKNOWN_COMPARISON; } -int filter_comparison(int orig, int op) +/* + * This is mostly used when you know from extra state that a <= b but you + * know from comparisons that a != b so then if take the intersection then + * we know that a < b. The name is taken from the fact that the intersection + * of < and <= is <. + */ +int comparison_intersection(int left_compare, int right_compare) { - if (orig == op) - return orig; + int LT, GT, EQ, NE, total; - orig = remove_unsigned_from_comparison(orig); - op = remove_unsigned_from_comparison(op); + if (left_compare == IMPOSSIBLE_COMPARISON || + right_compare == IMPOSSIBLE_COMPARISON) + return IMPOSSIBLE_COMPARISON; - switch (orig) { - case 0: - return op; + left_compare = remove_unsigned_from_comparison(left_compare); + right_compare = remove_unsigned_from_comparison(right_compare); + + LT = GT = EQ = NE = total = 0; + + /* Only one side is known. */ + if (!left_compare) + return right_compare; + if (!right_compare) + return left_compare; + + switch (left_compare) { case '<': - switch (op) { - case '<': - case SPECIAL_LTE: - case SPECIAL_NOTEQUAL: - return '<'; - } - return 0; + LT++; + total += 1; + break; case SPECIAL_LTE: - switch (op) { - case '<': - case SPECIAL_LTE: - case SPECIAL_EQUAL: - return op; - case SPECIAL_NOTEQUAL: - return '<'; - } - return 0; + LT++; + EQ++; + total += 2; + break; case SPECIAL_EQUAL: - switch (op) { - case SPECIAL_LTE: - case SPECIAL_EQUAL: - case SPECIAL_GTE: - case SPECIAL_UNSIGNED_LTE: - case SPECIAL_UNSIGNED_GTE: - return SPECIAL_EQUAL; - } - return 0; + EQ++; + total += 1; + break; case SPECIAL_NOTEQUAL: - switch (op) { - case '<': - case SPECIAL_LTE: - return '<'; - case SPECIAL_UNSIGNED_LT: - case SPECIAL_UNSIGNED_LTE: - return SPECIAL_UNSIGNED_LT; - case SPECIAL_NOTEQUAL: - return op; - case '>': - case SPECIAL_GTE: - return '>'; - case SPECIAL_UNSIGNED_GT: - case SPECIAL_UNSIGNED_GTE: - return SPECIAL_UNSIGNED_GT; - } - return 0; + NE++; + total += 1; + break; case SPECIAL_GTE: - switch (op) { - case SPECIAL_LTE: - return SPECIAL_EQUAL; - case '>': - case SPECIAL_GTE: - case SPECIAL_EQUAL: - return op; - case SPECIAL_NOTEQUAL: - return '>'; - } - return 0; + GT++; + EQ++; + total += 2; + break; case '>': - switch (op) { - case '>': - case SPECIAL_GTE: - case SPECIAL_NOTEQUAL: - return '>'; - } - return 0; - case SPECIAL_UNSIGNED_LT: - switch (op) { - case SPECIAL_UNSIGNED_LT: - case SPECIAL_UNSIGNED_LTE: - case SPECIAL_NOTEQUAL: - return SPECIAL_UNSIGNED_LT; - } - return 0; - case SPECIAL_UNSIGNED_LTE: - switch (op) { - case SPECIAL_UNSIGNED_LT: - case SPECIAL_UNSIGNED_LTE: - case SPECIAL_EQUAL: - return op; - case SPECIAL_NOTEQUAL: - return SPECIAL_UNSIGNED_LT; - case SPECIAL_UNSIGNED_GTE: - return SPECIAL_EQUAL; - } - return 0; - case SPECIAL_UNSIGNED_GTE: - switch (op) { - case SPECIAL_UNSIGNED_LTE: - return SPECIAL_EQUAL; - case SPECIAL_NOTEQUAL: - return SPECIAL_UNSIGNED_GT; - case SPECIAL_EQUAL: - case SPECIAL_UNSIGNED_GTE: - case SPECIAL_UNSIGNED_GT: - return op; - } - return 0; - case SPECIAL_UNSIGNED_GT: - switch (op) { - case SPECIAL_UNSIGNED_GT: - case SPECIAL_UNSIGNED_GTE: - case SPECIAL_NOTEQUAL: - return SPECIAL_UNSIGNED_GT; - } - return 0; + GT++; + total += 1; + break; + default: + return UNKNOWN_COMPARISON; } - return 0; + + switch (right_compare) { + case '<': + LT++; + total += 1; + break; + case SPECIAL_LTE: + LT++; + EQ++; + total += 2; + break; + case SPECIAL_EQUAL: + EQ++; + total += 1; + break; + case SPECIAL_NOTEQUAL: + NE++; + total += 1; + break; + case SPECIAL_GTE: + GT++; + EQ++; + total += 2; + break; + case '>': + GT++; + total += 1; + break; + default: + return UNKNOWN_COMPARISON; + } + + if (LT == 2) { + if (EQ == 2) + return SPECIAL_LTE; + return '<'; + } + + if (GT == 2) { + if (EQ == 2) + return SPECIAL_GTE; + return '>'; + } + if (EQ == 2) + return SPECIAL_EQUAL; + if (total == 2 && EQ && NE) + return IMPOSSIBLE_COMPARISON; + if (GT && LT) + return IMPOSSIBLE_COMPARISON; + if (GT && NE) + return '>'; + if (LT && NE) + return '<'; + if (NE == 2) + return SPECIAL_NOTEQUAL; + if (total == 2 && (LT || GT) && EQ) + return IMPOSSIBLE_COMPARISON; + + return UNKNOWN_COMPARISON; } -static void pre_merge_hook(struct sm_state *sm) +static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) { - struct compare_data *data = sm->state->data; - int other; + struct compare_data *data = cur->state->data; + int extra, new; + static bool in_recurse; if (!data) return; - other = get_comparison(data->left, data->right); - if (!other) + + if (in_recurse) + return; + in_recurse = true; + extra = comparison_from_extra(data->left, data->right); + in_recurse = false; + if (!extra) + return; + new = comparison_intersection(extra, data->comparison); + if (new == data->comparison) return; - set_state(compare_id, sm->name, NULL, + set_state(compare_id, cur->name, NULL, alloc_compare_state(data->left, data->left_var, data->left_vsl, - other, + new, data->right, data->right_var, data->right_vsl)); } @@ -546,13 +567,14 @@ struct smatch_state *merge_compare_states(struct smatch_state *s1, struct smatch struct compare_data *data = s1->data; int op; + if (!data) + return &undefined; + op = merge_comparisons(state_to_comparison(s1), state_to_comparison(s2)); - if (op) - return alloc_compare_state( - data->left, data->left_var, data->left_vsl, - op, - data->right, data->right_var, data->right_vsl); - return &undefined; + return alloc_compare_state( + data->left, data->left_var, data->left_vsl, + op, + data->right, data->right_var, data->right_vsl); } static struct smatch_state *alloc_link_state(struct string_list *links) @@ -690,7 +712,11 @@ static void match_inc(struct sm_state *sm, bool preserve) set_state(compare_id, tmp, NULL, new); break; default: - set_state(compare_id, tmp, NULL, &undefined); + new = alloc_compare_state( + data->left, data->left_var, data->left_vsl, + UNKNOWN_COMPARISON, + data->right, data->right_var, data->right_vsl); + set_state(compare_id, tmp, NULL, new); } } END_FOR_EACH_PTR(tmp); } @@ -704,7 +730,14 @@ static void match_dec(struct sm_state *sm, bool preserve) links = sm->state->data; FOR_EACH_PTR(links, tmp) { + struct compare_data *data; + struct smatch_state *new; + state = get_state(compare_id, tmp, NULL); + if (!state || !state->data) + continue; + + data = state->data; switch (state_to_comparison(state)) { case SPECIAL_EQUAL: @@ -712,9 +745,6 @@ static void match_dec(struct sm_state *sm, bool preserve) case SPECIAL_UNSIGNED_LTE: case '<': case SPECIAL_UNSIGNED_LT: { - struct compare_data *data = state->data; - struct smatch_state *new; - if (preserve) break; @@ -726,7 +756,11 @@ static void match_dec(struct sm_state *sm, bool preserve) break; } default: - set_state(compare_id, tmp, NULL, &undefined); + new = alloc_compare_state( + data->left, data->left_var, data->left_vsl, + UNKNOWN_COMPARISON, + data->right, data->right_var, data->right_vsl); + set_state(compare_id, tmp, NULL, new); } } END_FOR_EACH_PTR(tmp); } @@ -739,7 +773,20 @@ static void reset_sm(struct sm_state *sm) links = sm->state->data; FOR_EACH_PTR(links, tmp) { - set_state(compare_id, tmp, NULL, &undefined); + struct smatch_state *old, *new; + + old = get_state(compare_id, tmp, NULL); + if (!old || !old->data) { + new = &undefined; + } else { + struct compare_data *data = old->data; + + new = alloc_compare_state( + data->left, data->left_var, data->left_vsl, + UNKNOWN_COMPARISON, + data->right, data->right_var, data->right_vsl); + } + set_state(compare_id, tmp, NULL, new); } END_FOR_EACH_PTR(tmp); set_state(link_id, sm->name, sm->sym, &undefined); } @@ -849,7 +896,7 @@ static void match_preop(struct expression *expr) return; op = rl_comparison(left, right); - if (!op) + if (op == UNKNOWN_COMPARISON) return; add_comparison(expr->unop, op, parent->right); @@ -1016,8 +1063,8 @@ static void update_tf_links(struct stree *pre_stree, true_comparison = combine_comparisons(left_comparison, right_comparison); false_comparison = combine_comparisons(left_false_comparison, right_comparison); - true_comparison = filter_comparison(orig_comparison, true_comparison); - false_comparison = filter_comparison(orig_comparison, false_comparison); + true_comparison = comparison_intersection(orig_comparison, true_comparison); + false_comparison = comparison_intersection(orig_comparison, false_comparison); if (strcmp(left_var, right_var) > 0) { struct expression *tmp_expr = left_expr; @@ -1276,8 +1323,8 @@ static void handle_comparison(struct expression *left_expr, int op, struct expre } orig_comparison = get_comparison(left_expr, right_expr); - op = filter_comparison(orig_comparison, op); - false_op = filter_comparison(orig_comparison, false_op); + op = comparison_intersection(orig_comparison, op); + false_op = comparison_intersection(orig_comparison, false_op); snprintf(state_name, sizeof(state_name), "%s vs %s", left, right); true_state = alloc_compare_state( @@ -1334,7 +1381,6 @@ void __comparison_match_condition(struct expression *expr) handle_comparison(new_left, expr->op, new_right, NULL, NULL); } - redo = 0; left = strip_parens(expr->left); right = strip_parens(expr->right); @@ -1618,7 +1664,7 @@ int get_comparison_strings(const char *one, const char *two) int ret = 0; if (!one || !two) - return 0; + return UNKNOWN_COMPARISON; if (strcmp(one, two) == 0) return SPECIAL_EQUAL; @@ -1646,10 +1692,12 @@ static int get_comparison_helper(struct expression *a, struct expression *b, boo { char *one = NULL; char *two = NULL; - int ret = 0; + int ret = UNKNOWN_COMPARISON; + int extra = UNKNOWN_COMPARISON; - if (!a || !b) - return 0; + if (a == UNKNOWN_COMPARISON || + b == UNKNOWN_COMPARISON) + return UNKNOWN_COMPARISON; a = strip_parens(a); b = strip_parens(b); @@ -1677,7 +1725,7 @@ static int get_comparison_helper(struct expression *a, struct expression *b, boo ret = get_comparison_strings(one, two); } - if (!ret) + if (ret == UNKNOWN_COMPARISON) goto free; if ((is_plus_one(a) || is_minus_one(b)) && ret == '<') @@ -1685,15 +1733,14 @@ static int get_comparison_helper(struct expression *a, struct expression *b, boo else if ((is_minus_one(a) || is_plus_one(b)) && ret == '>') ret = SPECIAL_GTE; else - ret = 0; + ret = UNKNOWN_COMPARISON; free: free_string(one); free_string(two); - if (!ret && use_extra) - return comparison_from_extra(a, b); - return ret; + extra = comparison_from_extra(a, b); + return comparison_intersection(ret, extra); } int get_comparison(struct expression *a, struct expression *b) @@ -1905,11 +1952,11 @@ void __add_return_comparison(struct expression *call, const char *range) { struct expression *arg; int comparison; - char buf[4]; + char buf[16]; if (!str_to_comparison_arg(range, call, &comparison, &arg)) return; - snprintf(buf, sizeof(buf), "%s", show_special(comparison)); + snprintf(buf, sizeof(buf), "%s", show_comparison(comparison)); update_links_from_call(call, comparison, arg); add_comparison(call, comparison, arg); } @@ -1971,11 +2018,12 @@ static char *range_comparison_to_param_helper(struct expression *expr, char star continue; snprintf(buf, sizeof(buf), "%s orig", param->ident->name); compare = get_comparison_strings(var, buf); - if (!compare) + if (compare == UNKNOWN_COMPARISON || + compare == IMPOSSIBLE_COMPARISON) continue; - if (show_special(compare)[0] != starts_with) + if (show_comparison(compare)[0] != starts_with) continue; - snprintf(buf, sizeof(buf), "[%s$%d]", show_special(compare), i); + snprintf(buf, sizeof(buf), "[%s$%d]", show_comparison(compare), i); ret_str = alloc_sname(buf); break; } END_FOR_EACH_PTR(param); @@ -2006,9 +2054,10 @@ char *name_sym_to_param_comparison(const char *name, struct symbol *sym) continue; snprintf(buf, sizeof(buf), "%s orig", param->ident->name); compare = get_comparison_strings(name, buf); - if (!compare) + if (compare == UNKNOWN_COMPARISON || + compare == IMPOSSIBLE_COMPARISON) continue; - snprintf(buf, sizeof(buf), "[%s$%d]", show_special(compare), i); + snprintf(buf, sizeof(buf), "[%s$%d]", show_comparison(compare), i); return alloc_sname(buf); } END_FOR_EACH_PTR(param); @@ -2049,7 +2098,7 @@ char *expr_param_comparison(struct expression *expr, int ignore) compare = get_comparison_strings(var, buf); if (!compare) continue; - snprintf(buf, sizeof(buf), "[%s$%d]", show_special(compare), i); + snprintf(buf, sizeof(buf), "[%s$%d]", show_comparison(compare), i); ret_str = alloc_sname(buf); break; } END_FOR_EACH_PTR(param); @@ -2128,7 +2177,9 @@ static void match_call_info(struct expression *expr) if (!sm) continue; data = sm->state->data; - if (!data || !data->comparison) + if (!data || + data->comparison == UNKNOWN_COMPARISON || + data->comparison == IMPOSSIBLE_COMPARISON) continue; arg_name = expr_to_var(arg); if (!arg_name) @@ -2153,7 +2204,7 @@ static void match_call_info(struct expression *expr) right_name = get_printed_param_name(expr, right_vs->var, right_vs->sym); if (!right_name) goto free; - snprintf(info_buf, sizeof(info_buf), "%s %s", show_special(comparison), right_name); + snprintf(info_buf, sizeof(info_buf), "%s %s", show_comparison(comparison), right_name); sql_insert_caller_info(expr, PARAM_COMPARE, i, "$", info_buf); free: @@ -2203,7 +2254,7 @@ static void struct_member_callback(struct expression *call, int param, char *pri right_name = get_printed_param_name(call, right->var, right->sym); if (!right_name) continue; - snprintf(info_buf, sizeof(info_buf), "%s %s", show_special(data->comparison), right_name); + snprintf(info_buf, sizeof(info_buf), "%s %s", show_comparison(data->comparison), right_name); sql_insert_caller_info(call, PARAM_COMPARE, param, printed_name, info_buf); } END_FOR_EACH_PTR(link); } @@ -2274,7 +2325,9 @@ static void print_return_comparison(int return_id, char *return_ranges, struct e if (!sm) continue; data = sm->state->data; - if (!data || !data->comparison) + if (!data || + data->comparison == UNKNOWN_COMPARISON || + data->comparison == IMPOSSIBLE_COMPARISON) continue; if (ptr_list_size((struct ptr_list *)data->left_vsl) != 1 || ptr_list_size((struct ptr_list *)data->right_vsl) != 1) @@ -2316,7 +2369,7 @@ static void print_return_comparison(int return_id, char *return_ranges, struct e * smatch_param_compare_limit.c. */ - snprintf(info_buf, sizeof(info_buf), "%s %s", show_special(data->comparison), right_buf); + snprintf(info_buf, sizeof(info_buf), "%s %s", show_comparison(data->comparison), right_buf); sql_insert_return_states(return_id, return_ranges, PARAM_COMPARE, left_param, left_buf, info_buf); } END_FOR_EACH_PTR(link); @@ -2493,7 +2546,7 @@ int param_compare_limit_is_impossible(struct expression *expr, int left_param, c if (!state_op) goto free; - if (!filter_comparison(remove_unsigned_from_comparison(state_op), op)) + if (!comparison_intersection(remove_unsigned_from_comparison(state_op), op)) ret = 1; free: free_string(left_name); @@ -2591,37 +2644,47 @@ static void filter_by_sm(struct sm_state *sm, int op, struct state_list **false_stack) { struct compare_data *data; - int istrue = 0; - int isfalse = 0; + int is_true = 0; + int is_false = 0; if (!sm) return; data = sm->state->data; - if (!data) { - if (sm->merged) { - filter_by_sm(sm->left, op, true_stack, false_stack); - filter_by_sm(sm->right, op, true_stack, false_stack); - } + if (!data || data->comparison == UNKNOWN_COMPARISON) + goto split; + if (data->comparison == IMPOSSIBLE_COMPARISON) return; - } - if (data->comparison && - data->comparison == filter_comparison(data->comparison, op)) - istrue = 1; - - if (data->comparison && - data->comparison == filter_comparison(data->comparison, negate_comparison(op))) - isfalse = 1; + /* + * We want to check that "data->comparison" is totally inside "op". So + * if data->comparison is < and op is <= then that's true. Or if + * data->comparison is == and op is <= then that's true. But if + * data->comparison is <= and op is < than that's neither true nor + * false. + */ + if (data->comparison == comparison_intersection(data->comparison, op)) + is_true = 1; + if (data->comparison == comparison_intersection(data->comparison, negate_comparison(op))) + is_false = 1; + + if (debug_implied()) { + sm_msg("%s: %s: op = '%s' negated '%s'. true_intersect = '%s' false_insersect = '%s' sm = '%s'", + __func__, + sm->state->name, + alloc_sname(show_comparison(op)), + alloc_sname(show_comparison(negate_comparison(op))), + alloc_sname(show_comparison(comparison_intersection(data->comparison, op))), + alloc_sname(show_comparison(comparison_intersection(data->comparison, negate_comparison(op)))), + show_sm(sm)); + } - if (istrue) + if (is_true) add_ptr_list(true_stack, sm); - if (isfalse) + if (is_false) add_ptr_list(false_stack, sm); - - if (sm->merged) { - filter_by_sm(sm->left, op, true_stack, false_stack); - filter_by_sm(sm->right, op, true_stack, false_stack); - } +split: + filter_by_sm(sm->left, op, true_stack, false_stack); + filter_by_sm(sm->right, op, true_stack, false_stack); } struct sm_state *comparison_implication_hook(struct expression *expr, @@ -2665,7 +2728,7 @@ struct sm_state *comparison_implication_hook(struct expression *expr, if (!*true_stack && !*false_stack) return NULL; - if (option_debug) + if (debug_implied()) sm_msg("implications from comparison: (%s)", show_sm(sm)); return sm; diff --git a/usr/src/tools/smatch/src/smatch_conditions.c b/usr/src/tools/smatch/src/smatch_conditions.c index 3127b13f46..5a6f72d275 100644 --- a/usr/src/tools/smatch/src/smatch_conditions.c +++ b/usr/src/tools/smatch/src/smatch_conditions.c @@ -78,12 +78,12 @@ static int handle_zero_comparisons(struct expression *expr) struct expression *zero; // if left is zero or right is zero - if (is_zero(expr->left)) { + if (expr_is_zero(expr->left)) { zero = strip_expr(expr->left); if (zero->type != EXPR_VALUE) __split_expr(expr->left); tmp = expr->right; - } else if (is_zero(expr->right)) { + } else if (expr_is_zero(expr->right)) { zero = strip_expr(expr->left); if (zero->type != EXPR_VALUE) __split_expr(expr->right); diff --git a/usr/src/tools/smatch/src/smatch_data/db/clear_user_data.sh b/usr/src/tools/smatch/src/smatch_data/db/clear_user_data.sh index c0826ae67d..d5920bdb89 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/clear_user_data.sh +++ b/usr/src/tools/smatch/src/smatch_data/db/clear_user_data.sh @@ -1,5 +1,5 @@ #!/bin/bash -echo "delete from caller_info where type = 8017; delete from return_states where type = 8017;" | sqlite3 smatch_db.sqlite +echo "delete from caller_info where type = 8017; delete from return_states where type = 8017 or type = 9017;" | sqlite3 smatch_db.sqlite diff --git a/usr/src/tools/smatch/src/smatch_data/db/copy_function_pointers.pl b/usr/src/tools/smatch/src/smatch_data/db/copy_function_pointers.pl new file mode 100755 index 0000000000..6d4fa5d8b4 --- /dev/null +++ b/usr/src/tools/smatch/src/smatch_data/db/copy_function_pointers.pl @@ -0,0 +1,55 @@ +#!/usr/bin/perl -w + +use strict; +use DBI; + +my $db_file = shift; +if (!$db_file) { + print "usage: copy_function_pointers.pl <db file>\n"; + exit(0); +} + +my $db = DBI->connect("dbi:SQLite:$db_file", "", "", {AutoCommit => 0}); + +my ($select, $function, $ptr); + +$select = $db->prepare('SELECT DISTINCT function, ptr FROM function_ptr WHERE function LIKE "% %";'); + +my %ptrs; + +$select->execute(); +while (($function, $ptr) = $select->fetchrow_array()) { + $ptrs{"$function"}{'ptr'} = $ptr; + $ptrs{"$function"}{'done'} = 0; +} + +sub copy_functions($); +sub copy_functions($) +{ + my $src = shift; + + if ($ptrs{"$src"}{'done'}) { + return; + } + $ptrs{"$src"}{'done'} = 1; + + my $select = $db->prepare('SELECT distinct file, function FROM function_ptr WHERE ptr = ?;'); + my $insert = $db->prepare('INSERT OR IGNORE INTO function_ptr VALUES (?, ?, ?, 1);'); + + $select->execute($src); + while (my ($file, $function) = $select->fetchrow_array()) { + if ($function =~ / /) { + copy_functions($function); + next; + } + + $insert->execute($file, $function, $ptrs{"$src"}{'ptr'}); + } +} + +foreach my $key (keys(%ptrs)) { + copy_functions($key); +} + +$db->commit(); +$db->disconnect(); diff --git a/usr/src/tools/smatch/src/smatch_data/db/create_db.sh b/usr/src/tools/smatch/src/smatch_data/db/create_db.sh index 37ff637650..c75c2aad4c 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/create_db.sh +++ b/usr/src/tools/smatch/src/smatch_data/db/create_db.sh @@ -43,6 +43,7 @@ if [ "$PROJ" != "" ] ; then ${bin_dir}/fixup_${PROJ}.sh $db_file fi +${bin_dir}/copy_function_pointers.pl $db_file ${bin_dir}/remove_mixed_up_pointer_params.pl $db_file ${bin_dir}/delete_too_common_fn_ptr.sh $db_file ${bin_dir}/mark_function_ptrs_searchable.pl $db_file diff --git a/usr/src/tools/smatch/src/smatch_data/db/fill_db_sql.pl b/usr/src/tools/smatch/src/smatch_data/db/fill_db_sql.pl index 68a4c31613..a18065cd49 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/fill_db_sql.pl +++ b/usr/src/tools/smatch/src/smatch_data/db/fill_db_sql.pl @@ -7,7 +7,7 @@ my $project = shift; my $warns = shift; my $db_file = shift; -if (!defined($warns)) { +if (!defined($db_file)) { print "usage: $0 <-p=project> <smatch_warns.txt> <db_file>\n"; exit(1); } diff --git a/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh b/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh index 8a52d2585f..7eb910eb3f 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh +++ b/usr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh @@ -41,6 +41,8 @@ delete from caller_info where function = '__dev_queue_xmit' and type = 8017; delete from caller_info where function = '__netdev_start_xmit' and type = 8017; delete from caller_info where function = '(struct packet_type)->func' and type = 8017; delete from caller_info where function = '(struct bio)->bi_end_io' and type = 8017; +delete from caller_info where type = 8017 and key = '*\$->bi_private'; +delete from caller_info where type = 8017 and key = '\$->bi_private'; delete from caller_info where caller = 'NF_HOOK_COND' and type = 8017; delete from caller_info where caller = 'NF_HOOK' and type = 8017; /* comparison doesn't deal with chunks, I guess. */ @@ -50,6 +52,7 @@ delete from caller_info where caller = 'snd_ctl_elem_read' and function = '(stru delete from caller_info where function = 'nf_tables_newexpr' and type = 8017 and key = '\$->family'; delete from caller_info where caller = 'fb_set_var' and function = '(struct fb_ops)->fb_set_par' and type = 8017 and parameter = 0; delete from return_states where function = 'tty_lookup_driver' and parameter = 2 and type = 8017; +delete from caller_info where function = 'iomap_apply' and type = 8017 and key = '*\$'; insert into caller_info values ('userspace', '', 'compat_sys_ioctl', 0, 0, 8017, 0, '\$', '1'); insert into caller_info values ('userspace', '', 'compat_sys_ioctl', 0, 0, 8017, 1, '\$', '1'); @@ -174,6 +177,9 @@ delete from return_states where file = 'drivers/rapidio/rio-access.c' and return /* Smatch sucks at loops */ delete from return_states where function = 'ata_dev_next' and type = 103; +/* The problem is that parsing big function pointers is hard. */ +delete from return_states where function = 'vfs_get_tree' and type = 1024; + EOF # fixme: this is totally broken diff --git a/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes b/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes index 66fe421030..d6b8f8e5d1 100644 --- a/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes +++ b/usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes @@ -53,3 +53,7 @@ security_kernel_post_read_file s32min-(-1),1-s32max (-4095)-(-1) array_index_mask_nospec 0-u64max u64max array_index_mask_nospec 0-u32max u32max nla_len (-4)-65531[$0->nla_len\ -\ 4] 0-65531[$0->nla_len\ -\ 4] +__rounddown_pow_of_two 0-u64max 0-u64max[<=$0] +__roundup_pow_of_two 0-u64max 0-u64max[>=$0] +kthread_probe_data 0 0-u64max +bus_for_each_dev (-4095)-1 (-4095)-1[r\ $3] diff --git a/usr/src/tools/smatch/src/smatch_data/db/smdb.py b/usr/src/tools/smatch/src/smatch_data/db/smdb.py index c0b310b13b..04bff00be4 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/smdb.py +++ b/usr/src/tools/smatch/src/smatch_data/db/smdb.py @@ -7,6 +7,7 @@ import sqlite3 import sys import re +import subprocess try: con = sqlite3.connect('smatch_db.sqlite') @@ -25,6 +26,8 @@ def usage(): print "data_info <struct_type> <member> - information about a given data type" print "function_ptr <function> - which function pointers point to this" print "trace_param <function> <param> - trace where a parameter came from" + print "find_tagged <function> <param> - find the source of a tagged value (arm64)" + print "parse_warns_tagged <smatch_warns.txt> - parse warns file for summary of tagged issues (arm64)" print "locals <file> - print the local values in a file." sys.exit(1) @@ -267,7 +270,7 @@ def get_next_str(txt): if txt[0] == '-': parsed += 1 for char in txt[parsed:]: - if char == '-': + if char == '-' or char == '[': break parsed += 1 val = txt[:parsed] @@ -542,6 +545,107 @@ def function_type_value(struct_type, member): for txt in cur: print "%-30s | %-30s | %s | %s" %(txt[0], txt[1], txt[2], txt[3]) +def rl_too_big(txt): + rl = txt_to_rl(txt) + ret = "" + for idx in range(len(rl)): + cur_max = rl[idx][1] + if (cur_max > 0xFFFFFFFFFFFFFF): + return 1 + + return 0 + +def rl_has_min_untagged(txt): + rl = txt_to_rl(txt) + ret = "" + for idx in range(len(rl)): + cur_min = rl[idx][0] + if (cur_min == 0xff80000000000000): + return 1 + + return 0 + +def rl_is_tagged(txt): + if not rl_too_big(txt): + return 0 + + if rl_has_min_untagged(txt): + return 0 + + return 1 + +def rl_is_treat_untagged(txt): + if "[u]" in txt: + return 1; + + return 0 + +def parse_warns_tagged(filename): + proc = subprocess.Popen(['cat %s | grep "potentially tagged" | sort | uniq' %(filename)], shell=True, stdout=subprocess.PIPE) + while True: + line = proc.stdout.readline() + if not line: + break + + linepos = re.search("([^\s]+)", line).group(1) + groupre = re.search("potentially tagged address \(([^,]+), ([^,]+), ([^\)]+)\)", line) + groupre.group(1) + + func = groupre.group(1) + param = int(groupre.group(2)) + var = groupre.group(3) + + if ("end" in var or "size" in var or "len" in var): + continue + + print "\n%s (func: %s, param: %d:%s) may be caused by:" %(linepos, func, param, var) + + if (param != -1): + if not find_tagged(func, param, 0, []): + print " %s (param %d) (can't walk call tree)" % (func, param) + else: + print " %s (variable %s (can't walk call tree)" % (func, var) + +def find_tagged(func, param, caller_call_id, printed): + + callers = {} + cur = con.cursor() + ptrs = get_function_pointers(func) + found = 0 + + for ptr in ptrs: + cur.execute("select call_id, value from caller_info where function = '%s' and parameter=%d and type=%d" %(ptr, param, type_to_int("DATA_SOURCE"))) + + for row in cur: + if (row[1][0] == '$'): + if row[0] not in callers: + callers[row[0]] = {} + callers[row[0]]["param"] = int(row[1][1]) + + for ptr in ptrs: + cur.execute("select caller, call_id, value from caller_info where function = '%s' and parameter=%d and type=%d" %(ptr, param, type_to_int("USER_DATA"))) + + for row in cur: + if not rl_is_tagged(row[2]): + continue + if rl_is_treat_untagged(row[2]): + continue + found = 1 + if row[1] not in callers: + callers[row[1]] = {} + if "param" not in callers[row[1]]: + line = " %s (param ?) -> %s (param %d)" % (row[0], func, param) + if line not in printed: + printed.append(line) + print line + continue + if row[0] not in printed: + printed.append(row[0]) + if not find_tagged(row[0], callers[row[1]]["param"], row[1], printed): + print " %s (param %d)" % (row[0], param) + + return found + def trace_callers(func, param): sources = [] prev_type = 0 @@ -574,8 +678,8 @@ def trace_param_helper(func, param, indent = 0): sources = trace_callers(func, param) for path in sources: - if len(path[1]) and path[1][0] == 'p' and path[1][1] == ' ': - p = int(path[1][2:]) + if len(path[1]) and path[1][0] == '$': + p = int(re.findall('\d+', path[1][1:])[0]) trace_param_helper(path[0], p, indent + 2) elif len(path[0]) and path[0][0] == '%': print " %s%s" %(" " * indent, path[1]) @@ -641,6 +745,13 @@ elif sys.argv[1] == "data_info": elif sys.argv[1] == "call_tree": func = sys.argv[2] print_call_tree(func) +elif sys.argv[1] == "find_tagged": + func = sys.argv[2] + param = int(sys.argv[3]) + find_tagged(func, param, 0, []) +elif sys.argv[1] == "parse_warns_tagged": + filename = sys.argv[2] + parse_warns_tagged(filename) elif sys.argv[1] == "where": if len(sys.argv) == 3: struct_type = "%" @@ -677,9 +788,5 @@ elif sys.argv[1] == "constraint": struct_type = sys.argv[2] member = sys.argv[3] constraint(struct_type, member) -elif sys.argv[1] == "test": - filename = sys.argv[2] - func = sys.argv[3] - caller_info_values(filename, func) else: usage() diff --git a/usr/src/tools/smatch/src/smatch_data/db/vim_smdb b/usr/src/tools/smatch/src/smatch_data/db/vim_smdb index c169c9c5d9..c855c1b08f 100755 --- a/usr/src/tools/smatch/src/smatch_data/db/vim_smdb +++ b/usr/src/tools/smatch/src/smatch_data/db/vim_smdb @@ -26,7 +26,21 @@ next=$(($i + 1)) rm -f $DIR/$next rm -f $DIR/.${i}.swp -smdb $* > $DIR/$i + +func="" +if [[ "$3" != "" ]] ; then + func="$3" +elif [[ "$2" != "" ]] ; then + func="$2" +elif [[ "$1" != "" ]] ; then + func="$1" +fi + +echo "$func" >> $DIR/history +tail -n 7 $DIR/history | tac | perl -ne 's/\n/ /; print' | perl -ne 's/ $//; print' > $DIR/$i +echo "" >> $DIR/$i +echo "==========================" >> $DIR/$i +smdb $* >> $DIR/$i echo "$DIR/$i" > $DIR/cur diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.bit_shifters.remove b/usr/src/tools/smatch/src/smatch_data/kernel.bit_shifters.remove index ec7ae50fba..b986da9fa1 100644 --- a/usr/src/tools/smatch/src/smatch_data/kernel.bit_shifters.remove +++ b/usr/src/tools/smatch/src/smatch_data/kernel.bit_shifters.remove @@ -5,5 +5,7 @@ V4L2_TUNER_MODE_LANG2 2 V4L2_TUNER_MODE_SAP 2 V4L2_TUNER_MODE_LANG1 3 V4L2_TUNER_MODE_LANG1_LANG2 4 - MBX_INTERRUPT 1 +HWTSTAMP_TX_ON 1 +LOCK_USAGE_READ_MASK 1 +LOCK_USAGE_DIR_MASK 2 diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.check_string_condition.ignore b/usr/src/tools/smatch/src/smatch_data/kernel.check_string_condition.ignore index 19db8f357a..3fd10f4c24 100644 --- a/usr/src/tools/smatch/src/smatch_data/kernel.check_string_condition.ignore +++ b/usr/src/tools/smatch/src/smatch_data/kernel.check_string_condition.ignore @@ -1 +1,2 @@ TRACE_EVENT +WARN_ON_ONCE diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.ignore_casted_params b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_casted_params index 4981799f70..7f5de081ce 100644 --- a/usr/src/tools/smatch/src/smatch_data/kernel.ignore_casted_params +++ b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_casted_params @@ -2,6 +2,7 @@ set_bit clear_bit __clear_bit __set_bit +test_bit test_and_set_bit find_last_bit change_bit diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.ignore_side_effects b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_side_effects index b4237017c0..05d50066a9 100644 --- a/usr/src/tools/smatch/src/smatch_data/kernel.ignore_side_effects +++ b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_side_effects @@ -8,6 +8,7 @@ ADD_STA_STATS ARCH_DLINFO AWDATA +CONVERT_COMMON_TCP_SOCK_FIELDS ENCODE ENCODE_DATA ENCODE_STR @@ -50,6 +51,7 @@ RADEON_PURGE_ZCACHE RADEON_WAIT_UNTIL_2D_IDLE RADEON_WAIT_UNTIL_3D_IDLE RADEON_WAIT_UNTIL_IDLE +rcu_assign_pointer RCU_INIT_POINTER READ64 rtnl_dereference @@ -67,5 +69,6 @@ SOCK_OPS_GET_TCP32 unsafe_get_user unsafe_put_user VIA_OUT_RING_QW +WREG32_SOC15_DPG_MODE_2_0 WRITE64 Z diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.ignore_uninitialized_param b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_uninitialized_param index 16825e753a..dedc55d59b 100644 --- a/usr/src/tools/smatch/src/smatch_data/kernel.ignore_uninitialized_param +++ b/usr/src/tools/smatch/src/smatch_data/kernel.ignore_uninitialized_param @@ -109,3 +109,6 @@ smb_hc_read 2 smb_word_op 5 atl1c_read_phy_reg 2 adf7242_read_reg 2 +tfp410_readb 2 +asus_wmi_get_devstate 2 +e1000_read_nvm 2 diff --git a/usr/src/tools/smatch/src/smatch_data/kernel.unreachable.ignore b/usr/src/tools/smatch/src/smatch_data/kernel.unreachable.ignore index f12b5b83d5..a2e29f3abd 100644 --- a/usr/src/tools/smatch/src/smatch_data/kernel.unreachable.ignore +++ b/usr/src/tools/smatch/src/smatch_data/kernel.unreachable.ignore @@ -23,3 +23,6 @@ iterate_all_kinds netdev_for_each_lower_dev for_each_clear_bit_from idr_for_each_entry_continue +for_each_engine_masked +drm_mm_for_each_hole +for_each_set_bit diff --git a/usr/src/tools/smatch/src/smatch_db.c b/usr/src/tools/smatch/src/smatch_db.c index d96c9ace99..18c5ce5025 100644 --- a/usr/src/tools/smatch/src/smatch_db.c +++ b/usr/src/tools/smatch/src/smatch_db.c @@ -508,17 +508,19 @@ static int is_local_symbol(struct expression *expr) void sql_select_return_states(const char *cols, struct expression *call, int (*callback)(void*, int, char**, char**), void *info) { + struct expression *fn; int row_count = 0; if (is_fake_call(call)) return; - if (call->fn->type != EXPR_SYMBOL || !call->fn->symbol || is_local_symbol(call->fn)) { + fn = strip_expr(call->fn); + if (fn->type != EXPR_SYMBOL || !fn->symbol || is_local_symbol(fn)) { sql_select_return_states_pointer(cols, call, callback, info); return; } - if (inlinable(call->fn)) { + if (inlinable(fn)) { mem_sql(callback, info, "select %s from return_states where call_id = '%lu' order by return_id, type;", cols, (unsigned long)call); @@ -526,12 +528,12 @@ void sql_select_return_states(const char *cols, struct expression *call, } run_sql(get_row_count, &row_count, "select count(*) from return_states where %s;", - get_static_filter(call->fn->symbol)); + get_static_filter(fn->symbol)); if (row_count > 3000) return; run_sql(callback, info, "select %s from return_states where %s order by file, return_id, type;", - cols, get_static_filter(call->fn->symbol)); + cols, get_static_filter(fn->symbol)); } #define CALL_IMPLIES 0 @@ -718,6 +720,33 @@ struct range_list *db_return_vals_from_str(const char *fn_name) return ret_info.return_range_list; } +/* + * This is used when we have a function that takes a function pointer as a + * parameter. "frob(blah, blah, my_function);" We know that the return values + * from frob() come from my_funcion() so we want to find the possible returns + * of my_function(), but we don't know which arguments are passed to it. + * + */ +struct range_list *db_return_vals_no_args(struct expression *expr) +{ + struct return_info ret_info = {}; + + if (!expr || expr->type != EXPR_SYMBOL) + return NULL; + + ret_info.static_returns_call = expr; + ret_info.return_type = get_type(expr); + ret_info.return_type = get_real_base_type(ret_info.return_type); + if (!ret_info.return_type) + return NULL; + + run_sql(db_return_callback, &ret_info, + "select distinct return from return_states where %s;", + get_static_filter(expr->symbol)); + + return ret_info.return_range_list; +} + static void match_call_marker(struct expression *expr) { struct symbol *type; @@ -1106,8 +1135,7 @@ static void match_data_from_db(struct symbol *sym) if (ptr_list_size((struct ptr_list *)ptr_names) > 20) { __free_ptr_list((struct ptr_list **)&ptr_names); __free_ptr_list((struct ptr_list **)&ptr_names_done); - stree = __pop_fake_cur_stree(); - free_stree(&stree); + __free_fake_cur_stree(); return; } @@ -1124,6 +1152,7 @@ static void match_data_from_db(struct symbol *sym) __unnullify_path(); data.prev_func_id = -1; data.ignore = 0; + data.results = 0; FOR_EACH_PTR(ptr_names, ptr) { run_sql(caller_info_callback, &data, @@ -1261,6 +1290,29 @@ static void match_call_implies(struct symbol *sym) call_implies_callbacks); } +static char *get_fn_param_str(struct expression *expr) +{ + struct expression *tmp; + int param; + char buf[32]; + + tmp = get_assigned_expr(expr); + if (tmp) + expr = tmp; + expr = strip_expr(expr); + if (!expr || expr->type != EXPR_CALL) + return NULL; + expr = strip_expr(expr->fn); + if (!expr || expr->type != EXPR_SYMBOL) + return NULL; + param = get_param_num(expr); + if (param < 0) + return NULL; + + snprintf(buf, sizeof(buf), "[r $%d]", param); + return alloc_sname(buf); +} + static char *get_return_compare_is_param(struct expression *expr) { char *var; @@ -1306,6 +1358,7 @@ static const char *get_return_ranges_str(struct expression *expr, struct range_l struct range_list *rl; char *return_ranges; sval_t sval; + char *fn_param_str; char *compare_str; char *math_str; char buf[128]; @@ -1321,6 +1374,7 @@ static const char *get_return_ranges_str(struct expression *expr, struct range_l return sval_to_str_or_err_ptr(sval); } + fn_param_str = get_fn_param_str(expr); compare_str = expr_equal_to_param(expr, -1); math_str = get_value_in_terms_of_parameter_math(expr); @@ -1337,6 +1391,10 @@ static const char *get_return_ranges_str(struct expression *expr, struct range_l } *rl_p = rl; + if (fn_param_str) { + snprintf(buf, sizeof(buf), "%s%s", return_ranges, fn_param_str); + return alloc_sname(buf); + } if (compare_str) { snprintf(buf, sizeof(buf), "%s%s", return_ranges, compare_str); return alloc_sname(buf); @@ -1851,8 +1909,6 @@ static int split_on_bool_sm(struct sm_state *sm, struct expression *expr) struct sm_state *tmp; int ret = 0; int nr_possible, nr_states; - char *compare_str = NULL; - char buf[128]; struct state_list *already_handled = NULL; if (!sm || !sm->merged) @@ -1881,12 +1937,6 @@ static int split_on_bool_sm(struct sm_state *sm, struct expression *expr) return_ranges = get_return_ranges_str(expr, &ret_rl); set_state(RETURN_ID, "return_ranges", NULL, alloc_estate_rl(ret_rl)); - compare_str = get_return_compare_str(expr); - if (compare_str) { - snprintf(buf, sizeof(buf), "%s%s", return_ranges, compare_str); - return_ranges = alloc_sname(buf); - } - return_id++; FOR_EACH_PTR(returned_state_callbacks, cb) { cb->callback(return_id, (char *)return_ranges, expr); @@ -2014,6 +2064,7 @@ vanilla: nr_states = get_db_state_count(); if (nr_states >= 10000) { match_return_info(return_id, (char *)return_ranges, expr); + print_limited_param_set(return_id, (char *)return_ranges, expr); mark_all_params_untracked(return_id, (char *)return_ranges, expr); return; } @@ -2287,25 +2338,38 @@ static char *get_next_string(char **str) static char string[256]; char *start; char *p = *str; - int len; + int len, i, j; if (*p == '\0') return NULL; start = p; - while (*p != '\0' && *p != ' ' && *p != '\n') + while (*p != '\0' && *p != '\n') { + if (*p == '\\' && *(p + 1) == ' ') { + p += 2; + continue; + } + if (*p == ' ') + break; p++; + } len = p - start; - if (len > 256) { - memcpy(string, start, 255); - string[255] = '\0'; + if (len >= sizeof(string)) { + memcpy(string, start, sizeof(string)); + string[sizeof(string) - 1] = '\0'; sm_ierror("return_fix: '%s' too long", string); **str = '\0'; return NULL; } memcpy(string, start, len); string[len] = '\0'; + for (i = 0; i < sizeof(string) - 1; i++) { + if (string[i] == '\\' && string[i + 1] == ' ') { + for (j = i; string[j] != '\0'; j++) + string[j] = string[j + 1]; + } + } if (*p != '\0') p++; *str = p; @@ -2499,14 +2563,14 @@ const char *state_name_to_param_name(const char *state_name, const char *param_n if (strcmp(state_name, param_name) == 0) { snprintf(buf, sizeof(buf), "%s$", add_star ? "*" : ""); - return buf; + return alloc_sname(buf); } if (state_name[name_len] == '-' && /* check for '-' from "->" */ strncmp(state_name, param_name, name_len) == 0) { snprintf(buf, sizeof(buf), "%s$%s", add_star ? "*" : "", state_name + name_len); - return buf; + return alloc_sname(buf); } return NULL; } diff --git a/usr/src/tools/smatch/src/smatch_estate.c b/usr/src/tools/smatch/src/smatch_estate.c index 533cd2d9e0..4882347776 100644 --- a/usr/src/tools/smatch/src/smatch_estate.c +++ b/usr/src/tools/smatch/src/smatch_estate.c @@ -53,6 +53,9 @@ struct smatch_state *merge_estates(struct smatch_state *s1, struct smatch_state if (estate_capped(s1) && estate_capped(s2)) estate_set_capped(tmp); + if (estate_treat_untagged(s1) && estate_treat_untagged(s2)) + estate_set_treat_untagged(tmp); + return tmp; } @@ -116,7 +119,7 @@ void estate_clear_fuzzy_max(struct smatch_state *state) int estate_has_hard_max(struct smatch_state *state) { - if (!state) + if (!state || !estate_rl(state)) return 0; return get_dinfo(state)->hard_max; } @@ -154,6 +157,23 @@ void estate_set_capped(struct smatch_state *state) get_dinfo(state)->capped = true; } +bool estate_treat_untagged(struct smatch_state *state) +{ + if (!state) + return false; + + /* impossible states are capped */ + if (!estate_rl(state)) + return true; + + return get_dinfo(state)->treat_untagged; +} + +void estate_set_treat_untagged(struct smatch_state *state) +{ + get_dinfo(state)->treat_untagged = true; +} + sval_t estate_min(struct smatch_state *state) { return rl_min(estate_rl(state)); @@ -204,6 +224,8 @@ int estates_equiv(struct smatch_state *one, struct smatch_state *two) return 0; if (estate_capped(one) != estate_capped(two)) return 0; + if (estate_treat_untagged(one) != estate_treat_untagged(two)) + return 0; if (strcmp(one->name, two->name) == 0) return 1; return 0; @@ -234,6 +256,8 @@ int estate_get_single_value(struct smatch_state *state, sval_t *sval) { sval_t min, max; + if (!estate_rl(state)) + return 0; min = rl_min(estate_rl(state)); max = rl_max(estate_rl(state)); if (sval_cmp(min, max) != 0) diff --git a/usr/src/tools/smatch/src/smatch_expressions.c b/usr/src/tools/smatch/src/smatch_expressions.c index a0d05ed8cb..a65222848a 100644 --- a/usr/src/tools/smatch/src/smatch_expressions.c +++ b/usr/src/tools/smatch/src/smatch_expressions.c @@ -162,6 +162,82 @@ struct expression *string_expression(char *str) return ret; } +static struct expression *get_expression_from_base_and_str(struct expression *base, const char *addition) +{ + struct expression *ret = NULL; + struct token *token, *prev, *end; + char *alloc; + + if (addition[0] == '\0') + return base; + + alloc = alloc_string_newline(addition); + + token = tokenize_buffer(alloc, strlen(alloc), &end); + if (!token) + goto free; + if (token_type(token) != TOKEN_STREAMBEGIN) + goto free; + token = token->next; + + ret = base; + while (token_type(token) == TOKEN_SPECIAL && + (token->special == SPECIAL_DEREFERENCE || token->special == '.')) { + prev = token; + token = token->next; + if (token_type(token) != TOKEN_IDENT) + goto free; + switch (prev->special) { + case SPECIAL_DEREFERENCE: + ret = deref_expression(ret); + ret = member_expression(ret, '*', token->ident); + break; + case '.': + ret = member_expression(ret, '.', token->ident); + break; + default: + goto free; + } + token = token->next; + } + + if (token_type(token) != TOKEN_STREAMEND) + goto free; + +free: + free_string(alloc); + + return ret; +} + +struct expression *gen_expression_from_name_sym(const char *name, struct symbol *sym) +{ + struct expression *base; + int skip = 0; + struct expression *ret; + + if (!name || !sym) + return NULL; + + base = symbol_expression(sym); + while (name[skip] != '\0' && name[skip] != '.' && name[skip] != '-') + skip++; + + ret = get_expression_from_base_and_str(base, name + skip); + if (ret) { + char *new = expr_to_str(ret); + + /* + * FIXME: this sometimes changes "foo->bar.a.b->c" into + * "foo->bar.a.b.c". I don't know why... :( + * + */ + if (!new || strcmp(name, new) != 0) + return NULL; + } + return ret; +} + struct expression *gen_expression_from_key(struct expression *arg, const char *key) { struct expression *ret; diff --git a/usr/src/tools/smatch/src/smatch_extra.c b/usr/src/tools/smatch/src/smatch_extra.c index c756182355..3dd4aba7f2 100644 --- a/usr/src/tools/smatch/src/smatch_extra.c +++ b/usr/src/tools/smatch/src/smatch_extra.c @@ -99,12 +99,86 @@ void call_extra_nomod_hooks(const char *name, struct symbol *sym, struct express call_extra_hooks(extra_nomod_hooks, name, sym, expr, state); } +static void set_union_info(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) +{ + struct symbol *type, *tmp, *inner_type, *inner, *new_type; + struct expression *deref, *member_expr; + struct smatch_state *new; + int offset, inner_offset; + static bool in_recurse; + char *member_name; + + if (__in_fake_assign) + return; + + if (in_recurse) + return; + in_recurse = true; + + if (!expr || expr->type != EXPR_DEREF || !expr->member) + goto done; + offset = get_member_offset_from_deref(expr); + if (offset < 0) + goto done; + + deref = strip_expr(expr->deref); + type = get_type(deref); + if (type_is_ptr(type)) + type = get_real_base_type(type); + if (!type || type->type != SYM_STRUCT) + goto done; + + FOR_EACH_PTR(type->symbol_list, tmp) { + inner_type = get_real_base_type(tmp); + if (!inner_type || inner_type->type != SYM_UNION) + continue; + + inner = first_ptr_list((struct ptr_list *)inner_type->symbol_list); + if (!inner || !inner->ident) + continue; + + inner_offset = get_member_offset(type, inner->ident->name); + if (inner_offset < offset) + continue; + if (inner_offset > offset) + goto done; + + FOR_EACH_PTR(inner_type->symbol_list, inner) { + struct symbol *tmp_type; + + if (!inner->ident || inner->ident == expr->member) + continue; + tmp_type = get_real_base_type(inner); + if (tmp_type && tmp_type->type == SYM_STRUCT) + continue; + member_expr = deref; + if (tmp->ident) + member_expr = member_expression(member_expr, '.', tmp->ident); + member_expr = member_expression(member_expr, expr->op, inner->ident); + member_name = expr_to_var(member_expr); + if (!member_name) + continue; + new_type = get_real_base_type(inner); + new = alloc_estate_rl(cast_rl(new_type, estate_rl(state))); + set_extra_mod_helper(member_name, sym, member_expr, new); + free_string(member_name); + } END_FOR_EACH_PTR(inner); + } END_FOR_EACH_PTR(tmp); + +done: + in_recurse = false; +} + static bool in_param_set; void set_extra_mod_helper(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) { + if (!expr) + expr = gen_expression_from_name_sym(name, sym); remove_from_equiv(name, sym); + set_union_info(name, sym, expr, state); call_extra_mod_hooks(name, sym, expr, state); - if ((__in_fake_assign || in_param_set) && + update_mtag_data(expr, state); + if (in_param_set && estate_is_unknown(state) && !get_state(SMATCH_EXTRA, name, sym)) return; set_state(SMATCH_EXTRA, name, sym, state); @@ -172,7 +246,7 @@ free: return NULL; } -static char *get_long_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym) +static char *get_long_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym, bool use_stack) { struct expression *tmp; struct sm_state *sm; @@ -196,6 +270,8 @@ static char *get_long_name_sym(const char *name, struct symbol *sym, struct symb return NULL; found: + if (!use_stack && name[tmp->symbol->ident->len] != '-') + return NULL; snprintf(buf, sizeof(buf), "%s%s", sm->name, name + tmp->symbol->ident->len); *new_sym = sm->sym; return alloc_string(buf); @@ -224,7 +300,7 @@ char *get_other_name_sym_helper(const char *name, struct symbol *sym, struct sym if (len >= sizeof(buf) - 2) return NULL; - while (len >= 1) { + while (use_stack && len >= 1) { if (buf[len] == '>' && buf[len - 1] == '-') { len--; buf[len] = '\0'; @@ -235,7 +311,7 @@ char *get_other_name_sym_helper(const char *name, struct symbol *sym, struct sym len--; } - ret = get_long_name_sym(name, sym, new_sym); + ret = get_long_name_sym(name, sym, new_sym, use_stack); if (ret) return ret; @@ -258,9 +334,9 @@ void set_extra_mod(const char *name, struct symbol *sym, struct expression *expr struct symbol *new_sym; set_extra_mod_helper(name, sym, expr, state); - new_name = get_other_name_sym(name, sym, &new_sym); + new_name = get_other_name_sym_nostack(name, sym, &new_sym); if (new_name && new_sym) - set_extra_mod_helper(new_name, new_sym, expr, state); + set_extra_mod_helper(new_name, new_sym, NULL, state); free_string(new_name); } @@ -1231,20 +1307,14 @@ static void asm_expr(struct statement *stmt) struct expression *expr; struct symbol *type; - int state = 0; FOR_EACH_PTR(stmt->asm_outputs, expr) { - switch (state) { - case 0: /* identifier */ - case 1: /* constraint */ - state++; - continue; - case 2: /* expression */ - state = 0; - type = get_type(strip_expr(expr)); - set_extra_expr_mod(expr, alloc_estate_whole(type)); + if (expr->type != EXPR_ASM_OPERAND) { + sm_perror("unexpected asm param type %d", expr->type); continue; } + type = get_type(strip_expr(expr->expr)); + set_extra_expr_mod(expr->expr, alloc_estate_whole(type)); } END_FOR_EACH_PTR(expr); } @@ -1280,6 +1350,8 @@ static void match_dereferences(struct expression *expr) { if (expr->type != EXPR_PREOP) return; + if (getting_address(expr)) + return; /* it's saying that foo[1] = bar dereferences foo[1] */ if (is_array(expr)) return; @@ -2203,7 +2275,7 @@ int parent_is_null_var_sym(const char *name, struct symbol *sym) start = &buf[0]; while (*start == '*') { start++; - state = get_state(SMATCH_EXTRA, start, sym); + state = __get_state(SMATCH_EXTRA, start, sym); if (!state) continue; if (!estate_rl(state)) @@ -2373,12 +2445,14 @@ static void struct_member_callback(struct expression *call, int param, char *pri struct range_list *rl; sval_t dummy; - if (estate_is_whole(sm->state)) + if (estate_is_whole(sm->state) || !estate_rl(sm->state)) return; if (filter_unused_param_value_info(call, param, printed_name, sm)) return; rl = estate_rl(sm->state); rl = intersect_with_real_abs_var_sym(sm->name, sm->sym, rl); + if (!rl) + return; sql_insert_caller_info(call, PARAM_VALUE, param, printed_name, show_rl(rl)); if (!estate_get_single_value(sm->state, &dummy)) { if (estate_has_hard_max(sm->state)) @@ -2569,7 +2643,7 @@ static void db_param_filter(struct expression *expr, int param, char *key, char static void db_param_add_set(struct expression *expr, int param, char *key, char *value, enum info_type op) { - struct expression *arg; + struct expression *arg, *gen_expr; char *name; char *other_name = NULL; struct symbol *sym, *other_sym; @@ -2589,9 +2663,12 @@ static void db_param_add_set(struct expression *expr, int param, char *key, char arg_type = get_arg_type_from_key(expr->fn, param, arg, key); param_type = get_member_type_from_key(arg, key); + if (param_type && param_type->type == SYM_STRUCT) + return; name = get_variable_from_key(arg, key, &sym); if (!name || !sym) goto free; + gen_expr = gen_expression_from_key(arg, key); state = get_state(SMATCH_EXTRA, name, sym); if (state) @@ -2605,9 +2682,9 @@ static void db_param_add_set(struct expression *expr, int param, char *key, char new = rl_union(new, added); other_name = get_other_name_sym_nostack(name, sym, &other_sym); - set_extra_mod(name, sym, NULL, alloc_estate_rl(new)); + set_extra_mod(name, sym, gen_expr, alloc_estate_rl(new)); if (other_name && other_sym) - set_extra_mod(other_name, other_sym, NULL, alloc_estate_rl(new)); + set_extra_mod(other_name, other_sym, gen_expr, alloc_estate_rl(new)); free: free_string(other_name); free_string(name); diff --git a/usr/src/tools/smatch/src/smatch_extra.h b/usr/src/tools/smatch/src/smatch_extra.h index 6877053f86..40d59baf25 100644 --- a/usr/src/tools/smatch/src/smatch_extra.h +++ b/usr/src/tools/smatch/src/smatch_extra.h @@ -31,6 +31,7 @@ struct data_info { sval_t fuzzy_max; unsigned int hard_max:1; unsigned int capped:1; + unsigned int treat_untagged:1; }; DECLARE_ALLOCATOR(data_info); @@ -73,6 +74,8 @@ int possibly_false_rl_LR(int comparison, struct range_list *a, struct range_list int rl_has_sval(struct range_list *rl, sval_t sval); int ranges_equiv(struct data_range *one, struct data_range *two); +bool is_err_ptr(sval_t sval); + int rl_equiv(struct range_list *one, struct range_list *two); int is_whole_rl(struct range_list *rl); int is_unknown_ptr(struct range_list *rl); @@ -146,6 +149,8 @@ void estate_clear_hard_max(struct smatch_state *state); int estate_get_hard_max(struct smatch_state *state, sval_t *sval); bool estate_capped(struct smatch_state *state); void estate_set_capped(struct smatch_state *state); +bool estate_treat_untagged(struct smatch_state *state); +void estate_set_treat_untagged(struct smatch_state *state); int estate_get_single_value(struct smatch_state *state, sval_t *sval); struct smatch_state *get_implied_estate(struct expression *expr); @@ -211,6 +216,7 @@ struct expression *string_expression(char *str); struct expression *compare_expression(struct expression *left, int op, struct expression *right); struct expression *unknown_value_expression(struct expression *expr); int is_fake_call(struct expression *expr); +struct expression *gen_expression_from_name_sym(const char *name, struct symbol *sym); struct expression *gen_expression_from_key(struct expression *arg, const char *key); void free_tmp_expressions(void); void expr_set_parent_expr(struct expression *expr, struct expression *parent); diff --git a/usr/src/tools/smatch/src/smatch_flow.c b/usr/src/tools/smatch/src/smatch_flow.c index 11a70bdd70..4a842027e3 100644 --- a/usr/src/tools/smatch/src/smatch_flow.c +++ b/usr/src/tools/smatch/src/smatch_flow.c @@ -44,6 +44,7 @@ static unsigned int loop_count; static int last_goto_statement_handled; int __expr_stmt_count; int __in_function_def; +int __in_unmatched_hook; static struct expression_list *switch_expr_stack = NULL; static struct expression_list *post_op_stack = NULL; @@ -460,6 +461,8 @@ void __split_expr(struct expression *expr) __fake_struct_member_assignments(expr); + /* Re-examine ->right for inlines. See the commit message */ + right = strip_expr(expr->right); if (expr->op == '=' && right->type == EXPR_CALL) __pass_to_client(expr, CALL_ASSIGNMENT_HOOK); @@ -527,8 +530,8 @@ void __split_expr(struct expression *expr) break; if (handle__builtin_choose_expr(expr)) break; - split_expr_list(expr->args, expr); __split_expr(expr->fn); + split_expr_list(expr->args, expr); if (is_inline_func(expr->fn)) add_inline_function(expr->fn->symbol); if (inlinable(expr->fn)) @@ -569,6 +572,7 @@ void __split_expr(struct expression *expr) default: break; }; + __pass_to_client(expr, EXPR_HOOK_AFTER); pop_expression(&big_expression_stack); } @@ -700,7 +704,7 @@ static void handle_post_loop(struct statement *stmt) __merge_gotos(loop_name, NULL); __split_stmt(stmt->iterator_statement); __merge_continues(); - if (!is_zero(stmt->iterator_post_condition)) + if (!expr_is_zero(stmt->iterator_post_condition)) __save_gotos(loop_name, NULL); if (is_forever_loop(stmt)) { @@ -1614,21 +1618,20 @@ static void split_function(struct symbol *sym) static void save_flow_state(void) { - __add_ptr_list(&backup, INT_PTR(loop_num << 2), 0); - __add_ptr_list(&backup, INT_PTR(loop_count << 2), 0); - __add_ptr_list(&backup, INT_PTR(final_pass << 2), 0); - - __add_ptr_list(&backup, big_statement_stack, 0); - __add_ptr_list(&backup, big_expression_stack, 0); - __add_ptr_list(&backup, big_condition_stack, 0); - __add_ptr_list(&backup, switch_expr_stack, 0); + __add_ptr_list(&backup, INT_PTR(loop_num << 2)); + __add_ptr_list(&backup, INT_PTR(loop_count << 2)); + __add_ptr_list(&backup, INT_PTR(final_pass << 2)); - __add_ptr_list(&backup, cur_func_sym, 0); + __add_ptr_list(&backup, big_statement_stack); + __add_ptr_list(&backup, big_expression_stack); + __add_ptr_list(&backup, big_condition_stack); + __add_ptr_list(&backup, switch_expr_stack); - __add_ptr_list(&backup, __prev_stmt, 0); - __add_ptr_list(&backup, __cur_stmt, 0); - __add_ptr_list(&backup, __next_stmt, 0); + __add_ptr_list(&backup, cur_func_sym); + __add_ptr_list(&backup, __prev_stmt); + __add_ptr_list(&backup, __cur_stmt); + __add_ptr_list(&backup, __next_stmt); } static void *pop_backup(void) diff --git a/usr/src/tools/smatch/src/smatch_function_hooks.c b/usr/src/tools/smatch/src/smatch_function_hooks.c index 983befeac1..2ff19afb07 100644 --- a/usr/src/tools/smatch/src/smatch_function_hooks.c +++ b/usr/src/tools/smatch/src/smatch_function_hooks.c @@ -373,7 +373,7 @@ static void store_return_state(struct db_callback_info *db_info, const char *ret db_info->ret_state = state; } -static bool fake_a_param_assignment(struct expression *expr, const char *return_str) +static bool fake_a_param_assignment(struct expression *expr, const char *return_str, struct smatch_state *orig) { struct expression *arg, *left, *right, *tmp, *fake_assign; char *p; @@ -437,6 +437,26 @@ static bool fake_a_param_assignment(struct expression *expr, const char *return_ __in_fake_parameter_assign++; __split_expr(fake_assign); __in_fake_parameter_assign--; + + /* + * If the return is "0-65531[$0->nla_len - 4]" the faked expression + * is maybe (-4)-65531 but we know it is in the 0-65531 range so both + * parts have to be considered. We use _nomod() because it's not really + * another modification, it's just a clarification. + * + */ + if (estate_rl(orig)) { + struct smatch_state *faked; + struct range_list *rl; + + faked = get_extra_state(left); + if (estate_rl(faked)) { + rl = rl_intersection(estate_rl(faked), estate_rl(orig)); + if (rl) + set_extra_expr_nomod(expr, alloc_estate_rl(rl)); + } + } + return true; } @@ -449,9 +469,10 @@ static void set_return_assign_state(struct db_callback_info *db_info) return; state = alloc_estate_rl(cast_rl(get_type(expr), clone_rl(estate_rl(db_info->ret_state)))); - set_extra_expr_mod(expr, state); + if (!fake_a_param_assignment(db_info->expr, db_info->ret_str, state)) + set_extra_expr_mod(expr, state); + db_info->ret_state = NULL; - fake_a_param_assignment(db_info->expr, db_info->ret_str); db_info->ret_str = NULL; } @@ -1092,7 +1113,6 @@ static int db_return_states_callback(void *_info, int argc, char **argv, char ** __add_return_to_param_mapping(db_info->expr, ret_str); } - FOR_EACH_PTR(db_return_states_list, tmp) { if (tmp->type == type) tmp->callback(db_info->expr, param, key, value); @@ -1170,12 +1190,14 @@ static void db_return_states_call(struct expression *expr) static void match_function_call(struct expression *expr) { struct call_back_list *call_backs; + struct expression *fn; - if (expr->fn->type == EXPR_SYMBOL && expr->fn->symbol) { - call_backs = search_callback(func_hash, (char *)expr->fn->symbol->ident->name); + fn = strip_expr(expr->fn); + if (fn->type == EXPR_SYMBOL && fn->symbol) { + call_backs = search_callback(func_hash, (char *)fn->symbol->ident->name); if (call_backs) call_call_backs(call_backs, REGULAR_CALL, - expr->fn->symbol->ident->name, expr); + fn->symbol->ident->name, expr); } db_return_states_call(expr); } diff --git a/usr/src/tools/smatch/src/smatch_function_ptrs.c b/usr/src/tools/smatch/src/smatch_function_ptrs.c index e8b616425b..fc9c51b6a6 100644 --- a/usr/src/tools/smatch/src/smatch_function_ptrs.c +++ b/usr/src/tools/smatch/src/smatch_function_ptrs.c @@ -50,6 +50,46 @@ static char *get_from__symbol_get(struct expression *expr) return alloc_string(arg->string->data); } +static int xxx_is_array(struct expression *expr) +{ + struct symbol *type; + + expr = strip_expr(expr); + if (!expr) + return 0; + + if (expr->type == EXPR_PREOP && expr->op == '*') { + expr = strip_expr(expr->unop); + if (!expr) + return 0; + if (expr->type == EXPR_BINOP && expr->op == '+') + return 1; + } + + if (expr->type != EXPR_BINOP || expr->op != '+') + return 0; + + type = get_type(expr->left); + if (!type) + return 0; + if (type->type != SYM_ARRAY && type->type != SYM_PTR) + return 0; + + return 1; +} + +static struct expression *xxx_get_array_base(struct expression *expr) +{ + if (!xxx_is_array(expr)) + return NULL; + expr = strip_expr(expr); + if (expr->type == EXPR_PREOP && expr->op == '*') + expr = strip_expr(expr->unop); + if (expr->type != EXPR_BINOP || expr->op != '+') + return NULL; + return strip_parens(expr->left); +} + static char *get_array_ptr(struct expression *expr) { struct expression *array; @@ -57,7 +97,7 @@ static char *get_array_ptr(struct expression *expr) char *name; char buf[256]; - array = get_array_base(expr); + array = xxx_get_array_base(expr); if (array) { name = get_member_name(array); @@ -78,7 +118,7 @@ static char *get_array_ptr(struct expression *expr) } expr = get_assigned_expr(expr); - array = get_array_base(expr); + array = xxx_get_array_base(expr); if (!array) return NULL; name = expr_to_var(array); @@ -141,7 +181,7 @@ char *get_fnptr_name(struct expression *expr) { char *name; - if (is_zero(expr)) + if (expr_is_zero(expr)) return NULL; expr = strip_expr(expr); @@ -255,6 +295,12 @@ static int can_hold_function_ptr(struct expression *expr) if (!type) return 0; } + /* pointer to a pointer */ + if (type->type == SYM_PTR || type->type == SYM_ARRAY) { + type = get_real_base_type(type); + if (!type) + return 0; + } if (type->type == SYM_FN) return 1; if (type == &ulong_ctype && expr->type == EXPR_DEREF) @@ -279,7 +325,8 @@ static void match_function_assign(struct expression *expr) right = strip_expr(right->unop); if (right->type != EXPR_SYMBOL && - right->type != EXPR_DEREF) + right->type != EXPR_DEREF && + right->type != EXPR_CALL) return; if (!can_hold_function_ptr(right) || diff --git a/usr/src/tools/smatch/src/smatch_helper.c b/usr/src/tools/smatch/src/smatch_helper.c index 81125ad308..ed53276516 100644 --- a/usr/src/tools/smatch/src/smatch_helper.c +++ b/usr/src/tools/smatch/src/smatch_helper.c @@ -39,6 +39,19 @@ char *alloc_string(const char *str) return tmp; } +char *alloc_string_newline(const char *str) +{ + char *tmp; + int len; + + if (!str) + return NULL; + len = strlen(str); + tmp = malloc(len + 2); + snprintf(tmp, len + 2, "%s\n", str); + return tmp; +} + void free_string(char *str) { free(str); @@ -276,10 +289,13 @@ static void __get_variable_from_expr(struct symbol **sym_ptr, char *buf, return; } case EXPR_VALUE: { + sval_t sval = {}; char tmp[25]; *complicated = 1; - snprintf(tmp, 25, "%lld", expr->value); + if (!get_value(expr, &sval)) + return; + snprintf(tmp, 25, "%s", sval_to_numstr(sval)); append(buf, tmp, len); return; } @@ -589,7 +605,7 @@ int sym_name_is(const char *name, struct expression *expr) return 0; } -int is_zero(struct expression *expr) +int expr_is_zero(struct expression *expr) { sval_t sval; @@ -818,24 +834,21 @@ int is_error_return(struct expression *expr) return 0; } -int getting_address(void) +int getting_address(struct expression *expr) { - struct expression *tmp; - int i = 0; - int dot_ops = 0; - - FOR_EACH_PTR_REVERSE(big_expression_stack, tmp) { - if (!i++) - continue; - if (tmp->type == EXPR_PREOP && tmp->op == '(') - continue; - if (tmp->op == '.' && !dot_ops++) - continue; - if (tmp->op == '&') - return 1; - return 0; - } END_FOR_EACH_PTR_REVERSE(tmp); - return 0; + int deref_count = 0; + + while ((expr = expr_get_parent_expr(expr))) { + if (expr->type == EXPR_PREOP && expr->op == '*') { + /* &foo->bar->baz dereferences "foo->bar" */ + if (deref_count == 0) + deref_count++; + return false; + } + if (expr->type == EXPR_PREOP && expr->op == '&') + return true; + } + return false; } int get_struct_and_member(struct expression *expr, const char **type, const char **member) diff --git a/usr/src/tools/smatch/src/smatch_hooks.c b/usr/src/tools/smatch/src/smatch_hooks.c index 3fb50feb5b..9df4a83f27 100644 --- a/usr/src/tools/smatch/src/smatch_hooks.c +++ b/usr/src/tools/smatch/src/smatch_hooks.c @@ -18,6 +18,7 @@ #include "smatch.h" enum data_type { + NO_DATA, EXPR_PTR, STMT_PTR, SYMBOL_PTR, @@ -26,15 +27,61 @@ enum data_type { struct hook_container { int hook_type; - enum data_type data_type; + int owner; void *fn; }; ALLOCATOR(hook_container, "hook functions"); DECLARE_PTR_LIST(hook_func_list, struct hook_container); + +typedef void (expr_func)(struct expression *expr); +typedef void (stmt_func)(struct statement *stmt); +typedef void (sym_func)(struct symbol *sym); +typedef void (sym_list_func)(struct symbol_list *sym_list); + static struct hook_func_list *merge_funcs; static struct hook_func_list *unmatched_state_funcs; static struct hook_func_list *hook_array[NUM_HOOKS] = {}; -void (**pre_merge_hooks)(struct sm_state *sm); +static const enum data_type data_types[NUM_HOOKS] = { + [EXPR_HOOK] = EXPR_PTR, + [EXPR_HOOK_AFTER] = EXPR_PTR, + [STMT_HOOK] = STMT_PTR, + [STMT_HOOK_AFTER] = STMT_PTR, + [SYM_HOOK] = EXPR_PTR, + [STRING_HOOK] = EXPR_PTR, + [DECLARATION_HOOK] = SYMBOL_PTR, + [ASSIGNMENT_HOOK] = EXPR_PTR, + [ASSIGNMENT_HOOK_AFTER] = EXPR_PTR, + [RAW_ASSIGNMENT_HOOK] = EXPR_PTR, + [GLOBAL_ASSIGNMENT_HOOK] = EXPR_PTR, + [CALL_ASSIGNMENT_HOOK] = EXPR_PTR, + [MACRO_ASSIGNMENT_HOOK] = EXPR_PTR, + [BINOP_HOOK] = EXPR_PTR, + [OP_HOOK] = EXPR_PTR, + [LOGIC_HOOK] = EXPR_PTR, + [PRELOOP_HOOK] = STMT_PTR, + [CONDITION_HOOK] = EXPR_PTR, + [SELECT_HOOK] = EXPR_PTR, + [WHOLE_CONDITION_HOOK] = EXPR_PTR, + [FUNCTION_CALL_HOOK] = EXPR_PTR, + [CALL_HOOK_AFTER_INLINE] = EXPR_PTR, + [FUNCTION_CALL_HOOK_AFTER_DB] = EXPR_PTR, + [DEREF_HOOK] = EXPR_PTR, + [CASE_HOOK] = NO_DATA, + [ASM_HOOK] = STMT_PTR, + [CAST_HOOK] = EXPR_PTR, + [SIZEOF_HOOK] = EXPR_PTR, + [BASE_HOOK] = SYMBOL_PTR, + [FUNC_DEF_HOOK] = SYMBOL_PTR, + [AFTER_DEF_HOOK] = SYMBOL_PTR, + [END_FUNC_HOOK] = SYMBOL_PTR, + [AFTER_FUNC_HOOK] = SYMBOL_PTR, + [RETURN_HOOK] = EXPR_PTR, + [INLINE_FN_START] = EXPR_PTR, + [INLINE_FN_END] = EXPR_PTR, + [END_FILE_HOOK] = SYM_LIST_PTR, +}; + +void (**pre_merge_hooks)(struct sm_state *cur, struct sm_state *other); struct scope_container { void *fn; @@ -51,123 +98,14 @@ void add_hook(void *func, enum hook_type type) container->hook_type = type; container->fn = func; - switch (type) { - case EXPR_HOOK: - container->data_type = EXPR_PTR; - break; - case STMT_HOOK: - container->data_type = STMT_PTR; - break; - case STMT_HOOK_AFTER: - container->data_type = STMT_PTR; - break; - case SYM_HOOK: - container->data_type = EXPR_PTR; - break; - case STRING_HOOK: - container->data_type = EXPR_PTR; - break; - case DECLARATION_HOOK: - container->data_type = SYMBOL_PTR; - break; - case ASSIGNMENT_HOOK: - container->data_type = EXPR_PTR; - break; - case ASSIGNMENT_HOOK_AFTER: - container->data_type = EXPR_PTR; - break; - case RAW_ASSIGNMENT_HOOK: - container->data_type = EXPR_PTR; - break; - case GLOBAL_ASSIGNMENT_HOOK: - container->data_type = EXPR_PTR; - break; - case CALL_ASSIGNMENT_HOOK: - container->data_type = EXPR_PTR; - break; - case MACRO_ASSIGNMENT_HOOK: - container->data_type = EXPR_PTR; - break; - case BINOP_HOOK: - container->data_type = EXPR_PTR; - break; - case OP_HOOK: - container->data_type = EXPR_PTR; - break; - case LOGIC_HOOK: - container->data_type = EXPR_PTR; - break; - case PRELOOP_HOOK: - container->data_type = STMT_PTR; - break; - case CONDITION_HOOK: - container->data_type = EXPR_PTR; - break; - case SELECT_HOOK: - container->data_type = EXPR_PTR; - break; - case WHOLE_CONDITION_HOOK: - container->data_type = EXPR_PTR; - break; - case FUNCTION_CALL_HOOK: - container->data_type = EXPR_PTR; - break; - case CALL_HOOK_AFTER_INLINE: - container->data_type = EXPR_PTR; - break; - case FUNCTION_CALL_HOOK_AFTER_DB: - container->data_type = EXPR_PTR; - break; - case DEREF_HOOK: - container->data_type = EXPR_PTR; - break; - case CASE_HOOK: - /* nothing needed */ - break; - case ASM_HOOK: - container->data_type = STMT_PTR; - break; - case CAST_HOOK: - container->data_type = EXPR_PTR; - break; - case SIZEOF_HOOK: - container->data_type = EXPR_PTR; - break; - case BASE_HOOK: - container->data_type = SYMBOL_PTR; - break; - case FUNC_DEF_HOOK: - container->data_type = SYMBOL_PTR; - break; - case AFTER_DEF_HOOK: - container->data_type = SYMBOL_PTR; - break; - case END_FUNC_HOOK: - container->data_type = SYMBOL_PTR; - break; - case AFTER_FUNC_HOOK: - container->data_type = SYMBOL_PTR; - break; - case RETURN_HOOK: - container->data_type = EXPR_PTR; - break; - case INLINE_FN_START: - container->data_type = EXPR_PTR; - break; - case INLINE_FN_END: - container->data_type = EXPR_PTR; - break; - case END_FILE_HOOK: - container->data_type = SYM_LIST_PTR; - break; - } + add_ptr_list(&hook_array[type], container); } void add_merge_hook(int client_id, merge_func_t *func) { struct hook_container *container = __alloc_hook_container(0); - container->data_type = client_id; + container->owner = client_id; container->fn = func; add_ptr_list(&merge_funcs, container); } @@ -175,53 +113,42 @@ void add_merge_hook(int client_id, merge_func_t *func) void add_unmatched_state_hook(int client_id, unmatched_func_t *func) { struct hook_container *container = __alloc_hook_container(0); - container->data_type = client_id; + container->owner = client_id; container->fn = func; add_ptr_list(&unmatched_state_funcs, container); } -void add_pre_merge_hook(int client_id, void (*hook)(struct sm_state *sm)) +void add_pre_merge_hook(int client_id, void (*hook)(struct sm_state *cur, struct sm_state *other)) { pre_merge_hooks[client_id] = hook; } -static void pass_to_client(void *fn) -{ - typedef void (expr_func)(); - ((expr_func *) fn)(); -} - static void pass_expr_to_client(void *fn, void *data) { - typedef void (expr_func)(struct expression *expr); - ((expr_func *) fn)((struct expression *) data); + ((expr_func *)fn)((struct expression *)data); } static void pass_stmt_to_client(void *fn, void *data) { - typedef void (stmt_func)(struct statement *stmt); - ((stmt_func *) fn)((struct statement *) data); + ((stmt_func *)fn)((struct statement *)data); } static void pass_sym_to_client(void *fn, void *data) { - typedef void (sym_func)(struct symbol *sym); - ((sym_func *) fn)((struct symbol *) data); + ((sym_func *)fn)((struct symbol *)data); } static void pass_sym_list_to_client(void *fn, void *data) { - typedef void (sym_func)(struct symbol_list *sym_list); - ((sym_func *) fn)((struct symbol_list *) data); + ((sym_list_func *)fn)((struct symbol_list *)data); } void __pass_to_client(void *data, enum hook_type type) { struct hook_container *container; - FOR_EACH_PTR(hook_array[type], container) { - switch (container->data_type) { + switch (data_types[type]) { case EXPR_PTR: pass_expr_to_client(container->fn, data); break; @@ -238,15 +165,6 @@ void __pass_to_client(void *data, enum hook_type type) } END_FOR_EACH_PTR(container); } -void __pass_to_client_no_data(enum hook_type type) -{ - struct hook_container *container; - - FOR_EACH_PTR(hook_array[type], container) { - pass_to_client(container->fn); - } END_FOR_EACH_PTR(container); -} - void __pass_case_to_client(struct expression *switch_expr, struct range_list *rl) { @@ -255,7 +173,7 @@ void __pass_case_to_client(struct expression *switch_expr, struct hook_container *container; FOR_EACH_PTR(hook_array[CASE_HOOK], container) { - ((case_func *) container->fn)(switch_expr, rl); + ((case_func *)container->fn)(switch_expr, rl); } END_FOR_EACH_PTR(container); } @@ -264,7 +182,7 @@ int __has_merge_function(int client_id) struct hook_container *tmp; FOR_EACH_PTR(merge_funcs, tmp) { - if (tmp->data_type == client_id) + if (tmp->owner == client_id) return 1; } END_FOR_EACH_PTR(tmp); return 0; @@ -285,8 +203,8 @@ struct smatch_state *__client_merge_function(int owner, } FOR_EACH_PTR(merge_funcs, tmp) { - if (tmp->data_type == owner) - return ((merge_func_t *) tmp->fn)(s1, s2); + if (tmp->owner == owner) + return ((merge_func_t *)tmp->fn)(s1, s2); } END_FOR_EACH_PTR(tmp); return &undefined; } @@ -296,19 +214,19 @@ struct smatch_state *__client_unmatched_state_function(struct sm_state *sm) struct hook_container *tmp; FOR_EACH_PTR(unmatched_state_funcs, tmp) { - if (tmp->data_type == sm->owner) - return ((unmatched_func_t *) tmp->fn)(sm); + if (tmp->owner == sm->owner) + return ((unmatched_func_t *)tmp->fn)(sm); } END_FOR_EACH_PTR(tmp); return &undefined; } -void call_pre_merge_hook(struct sm_state *sm) +void call_pre_merge_hook(struct sm_state *cur, struct sm_state *other) { - if (sm->owner >= num_checks) + if (cur->owner >= num_checks) return; - if (pre_merge_hooks[sm->owner]) - pre_merge_hooks[sm->owner](sm); + if (pre_merge_hooks[cur->owner]) + pre_merge_hooks[cur->owner](cur, other); } static struct scope_hook_list *pop_scope_hook_list(struct scope_hook_stack **stack) @@ -355,7 +273,7 @@ void __call_scope_hooks(void) hook_list = pop_scope_hook_list(&scope_hooks); FOR_EACH_PTR(hook_list, tmp) { - ((scope_hook *) tmp->fn)(tmp->data); + ((scope_hook *)tmp->fn)(tmp->data); __free_scope_container(tmp); } END_FOR_EACH_PTR(tmp); } diff --git a/usr/src/tools/smatch/src/smatch_implied.c b/usr/src/tools/smatch/src/smatch_implied.c index 86f682f851..b13a7cc049 100644 --- a/usr/src/tools/smatch/src/smatch_implied.c +++ b/usr/src/tools/smatch/src/smatch_implied.c @@ -58,7 +58,6 @@ * a pool: a pool is an slist that has been merged with another slist. */ -#include <sys/time.h> #include <time.h> #include "smatch.h" #include "smatch_slist.h" @@ -71,6 +70,11 @@ bool implications_off; #define implied_debug 0 #define DIMPLIED(msg...) do { if (implied_debug) printf(msg); } while (0) +bool debug_implied(void) +{ + return implied_debug; +} + /* * tmp_range_list(): * It messes things up to free range list allocations. This helper fuction @@ -298,13 +302,14 @@ static void __separate_pools(struct sm_state *sm, int comparison, struct range_l { int free_checked = 0; struct state_list *checked_states = NULL; - struct timeval now; + struct timeval now, diff; if (!sm) return; gettimeofday(&now, NULL); - if (now.tv_usec - start_time->tv_usec > 1000000) { + timersub(&now, start_time, &diff); + if (diff.tv_sec >= 1) { if (implied_debug) { sm_msg("debug: %s: implications taking too long. (%s %s %s)", __func__, sm->state->name, show_special(comparison), show_rl(rl)); @@ -451,14 +456,15 @@ struct sm_state *filter_pools(struct sm_state *sm, struct sm_state *left; struct sm_state *right; int removed = 0; - struct timeval now; + struct timeval now, diff; if (!sm) return NULL; if (*bail) return NULL; gettimeofday(&now, NULL); - if (now.tv_usec - start->tv_usec > 3000000) { + timersub(&now, start, &diff); + if (diff.tv_sec >= 3) { DIMPLIED("%s: implications taking too long: %s\n", __func__, sm_state_info(sm)); *bail = 1; return NULL; @@ -599,14 +605,6 @@ static void separate_and_filter(struct sm_state *sm, int comparison, struct rang *false_states = filter_stack(sm, pre_stree, true_stack, false_stack); free_slist(&true_stack); free_slist(&false_stack); - if (implied_debug) { - printf("These are the implied states for the true path: (%s (%s) %s %s)\n", - sm->name, sm->state->name, show_special(comparison), show_rl(rl)); - __print_stree(*true_states); - printf("These are the implied states for the false path: (%s (%s) %s %s)\n", - sm->name, sm->state->name, show_special(comparison), show_rl(rl)); - __print_stree(*false_states); - } gettimeofday(&time_after, NULL); sec = time_after.tv_sec - time_before.tv_sec; @@ -797,6 +795,12 @@ static int handled_by_extra_states(struct expression *expr, struct stree **implied_true, struct stree **implied_false) { + sval_t sval; + + /* If the expression is known then it has no implications. */ + if (get_implied_value(expr, &sval)) + return true; + if (expr->type == EXPR_COMPARE) return handle_comparison(expr, implied_true, implied_false); else @@ -883,6 +887,18 @@ static void set_implied_states(struct expression *expr) { struct sm_state *sm; + if (implied_debug && + (expr || saved_implied_true || saved_implied_false)) { + char *name; + + name = expr_to_str(expr); + printf("These are the implied states for the true path: (%s)\n", name); + __print_stree(saved_implied_true); + printf("These are the implied states for the false path: (%s)\n", name); + __print_stree(saved_implied_false); + free_string(name); + } + FOR_EACH_SM(saved_implied_true, sm) { __set_true_false_sm(sm, NULL); } END_FOR_EACH_SM(sm); diff --git a/usr/src/tools/smatch/src/smatch_integer_overflow.c b/usr/src/tools/smatch/src/smatch_integer_overflow.c index e164598aff..96fd25f4f1 100644 --- a/usr/src/tools/smatch/src/smatch_integer_overflow.c +++ b/usr/src/tools/smatch/src/smatch_integer_overflow.c @@ -184,7 +184,7 @@ int can_integer_overflow_expr(struct expression *expr) struct smatch_state *state; char *name; struct symbol *sym; - int ret; + int ret = 1; type = get_type(expr); if (!type) diff --git a/usr/src/tools/smatch/src/smatch_kernel_user_data.c b/usr/src/tools/smatch/src/smatch_kernel_user_data.c index dac5bf5208..ca4c7cd315 100644 --- a/usr/src/tools/smatch/src/smatch_kernel_user_data.c +++ b/usr/src/tools/smatch/src/smatch_kernel_user_data.c @@ -83,31 +83,23 @@ static struct smatch_state *empty_state(struct sm_state *sm) return alloc_estate_empty(); } -static void pre_merge_hook(struct sm_state *sm) +static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) { - struct smatch_state *user; + struct smatch_state *user = cur->state; struct smatch_state *extra; struct smatch_state *state; struct range_list *rl; - sval_t dummy; - sval_t sval_100; - sval_100.value = 100; - sval_100.type = &int_ctype; - - user = __get_state(my_id, sm->name, sm->sym); - if (!user || !estate_rl(user)) - return; - extra = __get_state(SMATCH_EXTRA, sm->name, sm->sym); + extra = __get_state(SMATCH_EXTRA, cur->name, cur->sym); if (!extra) return; rl = rl_intersection(estate_rl(user), estate_rl(extra)); - if (rl_to_sval(rl, &dummy)) - rl = NULL; state = alloc_estate_rl(clone_rl(rl)); - if (estate_capped(user) || is_capped_var_sym(sm->name, sm->sym)) + if (estate_capped(user) || is_capped_var_sym(cur->name, cur->sym)) estate_set_capped(state); - set_state(my_id, sm->name, sm->sym, state); + if (estate_treat_untagged(user)) + estate_set_treat_untagged(state); + set_state(my_id, cur->name, cur->sym, state); } static void extra_nomod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) @@ -124,6 +116,8 @@ static void extra_nomod_hook(const char *name, struct symbol *sym, struct expres new = alloc_estate_rl(rl); if (estate_capped(user)) estate_set_capped(new); + if (estate_treat_untagged(user)) + estate_set_treat_untagged(new); set_state(my_id, name, sym, new); } @@ -181,6 +175,28 @@ bool user_rl_capped(struct expression *expr) return true; /* not actually user data */ } +bool user_rl_treat_untagged(struct expression *expr) +{ + struct smatch_state *state; + struct range_list *rl; + sval_t sval; + + expr = strip_expr(expr); + if (!expr) + return false; + if (get_value(expr, &sval)) + return true; + + state = get_state_expr(my_id, expr); + if (state) + return estate_treat_untagged(state); + + if (get_user_rl(expr, &rl)) + return false; /* uncapped user data */ + + return true; /* not actually user data */ +} + static void tag_inner_struct_members(struct expression *expr, struct symbol *member) { struct expression *edge_member; @@ -546,7 +562,7 @@ static int handle_get_user(struct expression *expr) return 0; name = expr_to_var(expr->right); - if (!name || strcmp(name, "__val_gu") != 0) + if (!name || (strcmp(name, "__val_gu") != 0 && strcmp(name, "__gu_val") != 0)) goto free; set_state_expr(my_id, expr->left, alloc_estate_whole(get_type(expr->left))); ret = 1; @@ -582,6 +598,8 @@ static bool handle_op_assign(struct expression *expr) state = alloc_estate_rl(rl); if (user_rl_capped(binop_expr)) estate_set_capped(state); + if (user_rl_treat_untagged(expr->left)) + estate_set_treat_untagged(state); set_state_expr(my_id, expr->left, state); return true; } @@ -623,8 +641,11 @@ static void match_assign(struct expression *expr) rl = cast_rl(get_type(expr->left), rl); state = alloc_estate_rl(rl); + if (user_rl_capped(expr->right)) estate_set_capped(state); + if (user_rl_treat_untagged(expr->right)) + estate_set_treat_untagged(state); set_state_expr(my_id, expr->left, state); return; @@ -660,15 +681,10 @@ static void handle_eq_noteq(struct expression *expr) static struct range_list *strip_negatives(struct range_list *rl) { sval_t min = rl_min(rl); - sval_t minus_one; - sval_t over; + sval_t minus_one = { .type = rl_type(rl), .value = -1 }; + sval_t over = { .type = rl_type(rl), .value = INT_MAX + 1ULL }; sval_t max = sval_type_max(rl_type(rl)); - minus_one.type = rl_type(rl); - minus_one.value = INT_MAX + 1ULL; - over.type = rl_type(rl); - over.value = -1; - if (!rl) return NULL; @@ -1020,8 +1036,10 @@ static char *get_user_rl_str(struct expression *expr, struct symbol *type) if (!get_user_rl(expr, &rl)) return NULL; rl = cast_rl(type, rl); - snprintf(buf, sizeof(buf), "%s%s", - show_rl(rl), user_rl_capped(expr) ? "[c]" : ""); + snprintf(buf, sizeof(buf), "%s%s%s", + show_rl(rl), + user_rl_capped(expr) ? "[c]" : "", + user_rl_treat_untagged(expr) ? "[u]" : ""); return buf; } @@ -1093,8 +1111,9 @@ static void struct_member_callback(struct expression *call, int param, char *pri if (!rl) return; - snprintf(buf, sizeof(buf), "%s%s", show_rl(rl), - estate_capped(sm->state) ? "[c]" : ""); + snprintf(buf, sizeof(buf), "%s%s%s", show_rl(rl), + estate_capped(sm->state) ? "[c]" : "", + estate_treat_untagged(sm->state) ? "[u]" : ""); sql_insert_caller_info(call, USER_DATA, param, printed_name, buf); } @@ -1133,6 +1152,13 @@ static bool param_data_capped(const char *value) return false; } +static bool param_data_treat_untagged(const char *value) +{ + if (strstr(value, ",u") || strstr(value, "[u")) + return true; + return false; +} + static void set_param_user_data(const char *name, struct symbol *sym, char *key, char *value) { struct range_list *rl = NULL; @@ -1180,6 +1206,8 @@ static void set_param_user_data(const char *name, struct symbol *sym, char *key, state = alloc_estate_rl(rl); if (param_data_capped(value) || is_capped(expr)) estate_set_capped(state); + if (param_data_treat_untagged(value) || sym->ctype.as == 5) + estate_set_treat_untagged(state); set_state(my_id, fullname, sym, state); } @@ -1252,6 +1280,8 @@ static void set_to_user_data(struct expression *expr, char *key, char *value) state = alloc_estate_rl(rl); if (param_data_capped(value)) estate_set_capped(state); + if (param_data_treat_untagged(value)) + estate_set_treat_untagged(state); set_state(my_id, name, sym, state); free: free_string(name); @@ -1359,9 +1389,10 @@ static void param_set_to_user_data(int return_id, char *return_ranges, struct ex if (strcmp(param_name, "$") == 0) /* The -1 param is handled after the loop */ continue; - snprintf(buf, sizeof(buf), "%s%s", + snprintf(buf, sizeof(buf), "%s%s%s", show_rl(estate_rl(sm->state)), - estate_capped(sm->state) ? "[c]" : ""); + estate_capped(sm->state) ? "[c]" : "", + estate_treat_untagged(sm->state) ? "[u]" : ""); sql_insert_return_states(return_id, return_ranges, func_gets_user_data ? USER_DATA_SET : USER_DATA, param, param_name, buf); @@ -1381,9 +1412,10 @@ static void param_set_to_user_data(int return_id, char *return_ranges, struct ex return_found = true; if (strcmp(param_name, "*$") == 0) pointed_at_found = true; - snprintf(buf, sizeof(buf), "%s%s", + snprintf(buf, sizeof(buf), "%s%s%s", show_rl(estate_rl(sm->state)), - estate_capped(sm->state) ? "[c]" : ""); + estate_capped(sm->state) ? "[c]" : "", + estate_treat_untagged(sm->state) ? "[u]" : ""); sql_insert_return_states(return_id, return_ranges, func_gets_user_data ? USER_DATA_SET : USER_DATA, -1, param_name, buf); @@ -1391,8 +1423,10 @@ static void param_set_to_user_data(int return_id, char *return_ranges, struct ex /* This if for "return ntohl(foo);" */ if (!return_found && get_user_rl(expr, &rl)) { - snprintf(buf, sizeof(buf), "%s%s", - show_rl(rl), user_rl_capped(expr) ? "[c]" : ""); + snprintf(buf, sizeof(buf), "%s%s%s", + show_rl(rl), + user_rl_capped(expr) ? "[c]" : "", + user_rl_treat_untagged(expr) ? "[u]" : ""); sql_insert_return_states(return_id, return_ranges, func_gets_user_data ? USER_DATA_SET : USER_DATA, -1, "$", buf); @@ -1512,3 +1546,4 @@ void register_kernel_user_data2(int id) return; select_caller_info_hook(set_called, INTERNAL); } + diff --git a/usr/src/tools/smatch/src/smatch_local_values.c b/usr/src/tools/smatch/src/smatch_local_values.c deleted file mode 100644 index da0fcc4055..0000000000 --- a/usr/src/tools/smatch/src/smatch_local_values.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2013 Oracle. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt - */ - -#include "scope.h" -#include "smatch.h" -#include "smatch_slist.h" -#include "smatch_extra.h" - -static int my_id; - -/* - * I'm going to store the states of local data at the end of each function. - * Then at the end of the file, I'll combine the possible range lists for - * each state and store the value in the on-disk database. - * - * One issue is that when I read the data back from the in-memory database at - * the end of the file, then I don't have access to type information. I'll just - * cast everything to "long long" for now, I guess. We'll see how that works. - */ - -static char *db_vals; -static int get_vals(void *unused, int argc, char **argv, char **azColName) -{ - db_vals = alloc_string(argv[0]); - return 0; -} - -static int is_array_symbol(struct expression *expr) -{ - struct symbol *type; - - if (!expr || expr->type != EXPR_SYMBOL) - return 0; - type = get_type(expr); - if (!type) - return 0; - if (type->type == SYM_ARRAY) - return 1; - return 0; -} - -int get_local_rl(struct expression *expr, struct range_list **rl) -{ - char *name; - struct range_list *tmp; - - if (!is_static(expr)) - return 0; - if (is_array_symbol(expr)) - return 0; - name = expr_to_var(expr); - if (!name) - return 0; - - db_vals = NULL; - run_sql(get_vals, NULL, - "select value from local_values where file = '%s' and variable = '%s';", - get_filename(), name); - free_string(name); - if (!db_vals) - return 0; - str_to_rl(&llong_ctype, db_vals, &tmp); - *rl = cast_rl(get_type(expr), tmp); - free_string(db_vals); - - return 1; -} - -int get_local_max_helper(struct expression *expr, sval_t *sval) -{ - struct range_list *rl; - - if (!get_local_rl(expr, &rl)) - return 0; - *sval = rl_max(rl); - return 1; -} - -int get_local_min_helper(struct expression *expr, sval_t *sval) -{ - struct range_list *rl; - - if (!get_local_rl(expr, &rl)) - return 0; - *sval = rl_min(rl); - return 1; -} - -static struct smatch_state *unmatched_state(struct sm_state *sm) -{ - return alloc_estate_empty(); -} - -static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) -{ - struct smatch_state *old; - struct smatch_state *new; - - if (!sym || !(sym->ctype.modifiers & MOD_STATIC)) - return; - old = get_state(my_id, name, sym); - if (old) - new = merge_estates(old, state); - else - new = state; - set_state(my_id, name, sym, new); -} - -static void process_states(void) -{ - struct sm_state *sm; - struct smatch_state *extra; - struct range_list *rl; - - FOR_EACH_SM(__get_cur_stree(), sm) { - if (sm->owner != my_id) - continue; - extra = get_state(SMATCH_EXTRA, sm->name, sm->sym); - if (extra && estate_rl(extra)) - rl = rl_intersection(estate_rl(sm->state), estate_rl(extra)); - else - rl = estate_rl(sm->state); - rl = cast_rl(&llong_ctype, rl); - mem_sql(NULL, NULL, - "insert into local_values values ('%s', '%s', '%s', %lu);", - get_filename(), sm->name, show_rl(rl), - (unsigned long)sm->sym); - } END_FOR_EACH_SM(sm); -} - -static int get_initial_value_sym(struct symbol *sym, char *name, sval_t *sval) -{ - struct expression *expr_symbol, *deref, *tmp; - char *member_name; - - if (!sym) - return 0; - - if (!sym->initializer) { - *sval = sval_type_val(&llong_ctype, 0); - return 1; - } - if (sym->initializer->type != EXPR_INITIALIZER) - return get_value(sym->initializer, sval); - - expr_symbol = symbol_expression(sym); - FOR_EACH_PTR(sym->initializer->expr_list, tmp) { - if (tmp->type != EXPR_IDENTIFIER) /* how to handle arrays?? */ - continue; - deref = member_expression(expr_symbol, '.', tmp->expr_ident); - member_name = expr_to_var(deref); - if (!member_name) - continue; - if (strcmp(name, member_name) == 0) { - free_string(member_name); - return get_value(tmp->ident_expression, sval); - } - free_string(member_name); - } END_FOR_EACH_PTR(tmp); - - return 0; -} - -static char *cur_name; -static struct symbol *cur_symbol; -static struct range_list *cur_rl; -static void add_current_local(void) -{ - sval_t initial; - - if (!get_initial_value_sym(cur_symbol, cur_name, &initial)) { - free_string(cur_name); - cur_name = NULL; - cur_rl = NULL; - return; - } - add_range(&cur_rl, initial, initial); - if (!is_whole_rl(cur_rl)) - sql_insert_local_values(cur_name, show_rl(cur_rl)); - free_string(cur_name); - cur_name = NULL; - cur_rl = NULL; -} - -static int save_final_values(void *unused, int argc, char **argv, char **azColName) -{ - char *name = argv[0]; - char *sym_str = argv[1]; - char *value = argv[2]; - struct range_list *rl; - - if (!cur_name) { - cur_name = alloc_string(name); - cur_symbol = (struct symbol *)strtoul(sym_str, NULL, 10); - } else if (strcmp(cur_name, name) != 0) { - add_current_local(); - cur_name = alloc_string(name); - cur_symbol = (struct symbol *)strtoul(sym_str, NULL, 10); - cur_rl = NULL; - } - - str_to_rl(&llong_ctype, value, &rl); - cur_rl = rl_union(cur_rl, rl); - - return 0; -} - -static void match_end_file(struct symbol_list *sym_list) -{ - mem_sql(save_final_values, NULL, - "select distinct variable, symbol, value from local_values order by variable;"); - if (cur_name) - add_current_local(); -} - -void register_local_values(int id) -{ - my_id = id; - - if (!option_info) - return; - - set_dynamic_states(my_id); - add_extra_mod_hook(&extra_mod_hook); - add_unmatched_state_hook(my_id, &unmatched_state); - add_merge_hook(my_id, &merge_estates); - all_return_states_hook(&process_states); - add_hook(match_end_file, END_FILE_HOOK); - mem_sql(NULL, NULL, "alter table local_values add column symbol integer;"); -} diff --git a/usr/src/tools/smatch/src/smatch_math.c b/usr/src/tools/smatch/src/smatch_math.c index 9fb3429dd5..7ef474d680 100644 --- a/usr/src/tools/smatch/src/smatch_math.c +++ b/usr/src/tools/smatch/src/smatch_math.c @@ -31,6 +31,8 @@ static int get_absolute_rl_internal(struct expression *expr, struct range_list * static sval_t zero = {.type = &int_ctype, {.value = 0} }; static sval_t one = {.type = &int_ctype, {.value = 1} }; +static int fast_math_only; + struct range_list *rl_zero(void) { static struct range_list *zero_perm; @@ -856,7 +858,7 @@ static bool handle_conditional_rl(struct expression *expr, int implied, int *rec return get_rl_sval(expr->cond_false, implied, recurse_cnt, res, res_sval); /* this becomes a problem with deeply nested conditional statements */ - if (low_on_memory()) + if (fast_math_only || low_on_memory()) return false; type = get_type(expr); @@ -947,8 +949,6 @@ struct range_list *var_to_absolute_rl(struct expression *expr) state = get_real_absolute_state(expr); if (state && state->data && !estate_is_whole(state)) return clone_rl(estate_rl(state)); - if (get_local_rl(expr, &rl) && !is_whole_rl(rl)) - return rl; if (get_mtag_rl(expr, &rl)) return rl; if (get_db_type_rl(expr, &rl) && !is_whole_rl(rl)) @@ -1008,8 +1008,6 @@ static bool handle_variable(struct expression *expr, int implied, int *recurse_c if (!state) { if (implied == RL_HARD) return false; - if (get_local_rl(expr, res)) - return true; if (get_mtag_rl(expr, res)) return true; if (get_db_type_rl(expr, res)) @@ -1060,8 +1058,6 @@ static bool handle_variable(struct expression *expr, int implied, int *recurse_c return true; } - if (get_local_rl(expr, res)) - return true; if (get_mtag_rl(expr, res)) return true; if (get_db_type_rl(expr, res)) @@ -1516,6 +1512,16 @@ void clear_math_cache(void) memset(cached_results, 0, sizeof(cached_results)); } +void set_fast_math_only(void) +{ + fast_math_only++; +} + +void clear_fast_math_only(void) +{ + fast_math_only--; +} + /* * Don't cache EXPR_VALUE because values are fast already. * @@ -1760,6 +1766,12 @@ int known_condition_true(struct expression *expr) if (!expr) return 0; + if (__inline_fn && get_param_num(expr) >= 0) { + if (get_implied_value(expr, &tmp) && tmp.value) + return 1; + return 0; + } + if (get_value(expr, &tmp) && tmp.value) return 1; @@ -1768,10 +1780,18 @@ int known_condition_true(struct expression *expr) int known_condition_false(struct expression *expr) { + sval_t tmp; + if (!expr) return 0; - if (is_zero(expr)) + if (__inline_fn && get_param_num(expr) >= 0) { + if (get_implied_value(expr, &tmp) && tmp.value == 0) + return 1; + return 0; + } + + if (expr_is_zero(expr)) return 1; return 0; diff --git a/usr/src/tools/smatch/src/smatch_mem_tracker.c b/usr/src/tools/smatch/src/smatch_mem_tracker.c index 4f659a722e..a92b8f35cc 100644 --- a/usr/src/tools/smatch/src/smatch_mem_tracker.c +++ b/usr/src/tools/smatch/src/smatch_mem_tracker.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Oracle. + * Copyright 2019 Joyent, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,15 +19,18 @@ #include "smatch.h" #include <fcntl.h> #include <unistd.h> +#ifdef __sun #include <sys/procfs.h> +#endif static int my_id; -static int my_fd = -2; static unsigned long max_size; +#ifdef __sun unsigned long get_mem_kb(void) { + static int my_fd = -2; prpsinfo_t pbuf; if (my_fd == -2) { @@ -43,6 +47,24 @@ unsigned long get_mem_kb(void) return (pbuf.pr_rssize); } +#else +unsigned long get_mem_kb(void) +{ + FILE *file; + char buf[1024] = "0"; + unsigned long size; + + file = fopen("/proc/self/statm", "r"); + if (!file) + return 0; + fread(buf, 1, sizeof(buf), file); + fclose(file); + + size = strtoul(buf, NULL, 10); + size = size * sysconf(_SC_PAGESIZE) / 1024; + return size; +} +#endif static void match_end_func(struct symbol *sym) { diff --git a/usr/src/tools/smatch/src/smatch_modification_hooks.c b/usr/src/tools/smatch/src/smatch_modification_hooks.c index 07a32cdfbc..27456c6c52 100644 --- a/usr/src/tools/smatch/src/smatch_modification_hooks.c +++ b/usr/src/tools/smatch/src/smatch_modification_hooks.c @@ -143,9 +143,6 @@ static void call_modification_hooks(struct expression *expr, struct expression * char *name; struct symbol *sym; - if (late == LATE) - update_mtag_data(expr); - name = expr_to_known_chunk_sym(expr, &sym); if (!name) goto free; @@ -156,7 +153,7 @@ free: static void db_param_add(struct expression *expr, int param, char *key, char *value) { - struct expression *arg, *gen_expr; + struct expression *arg; char *name, *other_name; struct symbol *sym, *other_sym; @@ -169,10 +166,6 @@ static void db_param_add(struct expression *expr, int param, char *key, char *va if (!arg) return; - gen_expr = gen_expression_from_key(arg, key); - if (gen_expr) - update_mtag_data(gen_expr); - name = get_variable_from_key(arg, key, &sym); if (!name || !sym) goto free; @@ -226,23 +219,14 @@ static void match_call(struct expression *expr) static void asm_expr(struct statement *stmt, int late) { struct expression *expr; - int state = 0; FOR_EACH_PTR(stmt->asm_outputs, expr) { - switch (state) { - case 0: /* identifier */ - case 1: /* constraint */ - state++; - continue; - case 2: /* expression */ - state = 0; - call_modification_hooks(expr, NULL, late); + if (expr->type != EXPR_ASM_OPERAND) continue; - } + call_modification_hooks(expr->expr, NULL, late); } END_FOR_EACH_PTR(expr); } - static void match_assign_early(struct expression *expr) { match_assign(expr, EARLY); diff --git a/usr/src/tools/smatch/src/smatch_mtag.c b/usr/src/tools/smatch/src/smatch_mtag.c index b30e51bc2d..37d1f18579 100644 --- a/usr/src/tools/smatch/src/smatch_mtag.c +++ b/usr/src/tools/smatch/src/smatch_mtag.c @@ -347,7 +347,7 @@ int expr_to_mtag_offset(struct expression *expr, mtag_t *tag, int *offset) if (tmp < 0) return 0; tmp_offset += tmp; - expr = expr->deref; + expr = strip_expr(expr->deref); } *offset = tmp_offset; if (expr->type == EXPR_PREOP && expr->op == '*') { diff --git a/usr/src/tools/smatch/src/smatch_mtag_data.c b/usr/src/tools/smatch/src/smatch_mtag_data.c index 801542373b..c18a2e9725 100644 --- a/usr/src/tools/smatch/src/smatch_mtag_data.c +++ b/usr/src/tools/smatch/src/smatch_mtag_data.c @@ -69,6 +69,18 @@ static int is_kernel_param(const char *name) return 0; } +static bool is_ignored_macro(struct expression *expr) +{ + char *macro; + + macro = get_macro_name(expr->pos); + if (!macro) + return false; + if (strcmp(macro, "EXPORT_SYMBOL") == 0) + return true; + return false; +} + static void insert_mtag_data(mtag_t tag, int offset, struct range_list *rl) { rl = clone_rl_permanent(rl); @@ -79,14 +91,33 @@ static void insert_mtag_data(mtag_t tag, int offset, struct range_list *rl) tag, offset, DATA_VALUE, (unsigned long)rl); } -void update_mtag_data(struct expression *expr) +static bool invalid_type(struct symbol *type) +{ + if (!type) + return true; + if (type == &void_ctype) + return true; + if (type->type == SYM_STRUCT || + type->type == SYM_ARRAY || + type->type == SYM_UNION) + return true; + return false; +} + +void update_mtag_data(struct expression *expr, struct smatch_state *state) { - struct range_list *orig, *new, *rl; + struct range_list *orig, *new; struct symbol *type; char *name; mtag_t tag; int offset; + if (!expr) + return; + if (is_local_variable(expr)) + return; + if (is_ignored_macro(expr)) + return; name = expr_to_var(expr); if (is_kernel_param(name)) { free_string(name); @@ -98,15 +129,11 @@ void update_mtag_data(struct expression *expr) return; type = get_type(expr); - if ((offset == 0) && - (!type || type == &void_ctype || - type->type == SYM_STRUCT || type->type == SYM_UNION || type->type == SYM_ARRAY)) + if (offset == 0 && invalid_type(type)) return; - get_absolute_rl(expr, &rl); - orig = select_orig(tag, offset); - new = rl_union(orig, rl); + new = rl_union(orig, estate_rl(state)); insert_mtag_data(tag, offset, new); } @@ -117,6 +144,8 @@ static void match_global_assign(struct expression *expr) int offset; char *name; + if (is_ignored_macro(expr)) + return; name = expr_to_var(expr->left); if (is_kernel_param(name)) { free_string(name); @@ -188,10 +217,6 @@ static int get_rl_from_mtag_offset(mtag_t tag, int offset, struct symbol *type, int ret; int i; - if (!type || type == &void_ctype || - (type->type == SYM_STRUCT || type->type == SYM_ARRAY || type->type == SYM_UNION)) - return 0; - for (i = 0; i < ARRAY_SIZE(cached_results); i++) { if (merged == cached_results[i].tag) { if (cached_results[i].rl) { @@ -235,13 +260,15 @@ int get_mtag_rl(struct expression *expr, struct range_list **rl) mtag_t tag; int offset; + if (is_local_variable(expr)) + return 0; if (!expr_to_mtag_offset(expr, &tag, &offset)) return 0; if (offset >= MTAG_OFFSET_MASK) return 0; type = get_type(expr); - if (!type) + if (invalid_type(type)) return 0; return get_rl_from_mtag_offset(tag, offset, type, rl); diff --git a/usr/src/tools/smatch/src/smatch_nul_terminator.c b/usr/src/tools/smatch/src/smatch_nul_terminator.c index 86a3dbd4fb..31c6061cbd 100644 --- a/usr/src/tools/smatch/src/smatch_nul_terminator.c +++ b/usr/src/tools/smatch/src/smatch_nul_terminator.c @@ -69,6 +69,24 @@ static void match_nul_assign(struct expression *expr) set_terminated(array, &terminated); } +static struct smatch_state *get_terminated_state_var_sym(const char *name, struct symbol *sym) +{ + struct sm_state *sm, *tmp; + + sm = get_sm_state(my_id, name, sym); + if (!sm) + return NULL; + if (sm->state == &terminated || sm->state == &unterminated) + return sm->state; + + FOR_EACH_PTR(sm->possible, tmp) { + if (tmp->state == &unterminated) + return &unterminated; + } END_FOR_EACH_PTR(tmp); + + return NULL; +} + static struct smatch_state *get_terminated_state(struct expression *expr) { struct sm_state *sm, *tmp; @@ -240,6 +258,13 @@ free: free_string(name); } +bool is_nul_terminated_var_sym(const char *name, struct symbol *sym) +{ + if (get_terminated_state_var_sym(name, sym) == &terminated) + return 1; + return 0; +} + bool is_nul_terminated(struct expression *expr) { if (get_terminated_state(expr) == &terminated) diff --git a/usr/src/tools/smatch/src/smatch_param_filter.c b/usr/src/tools/smatch/src/smatch_param_filter.c index 9b5a04b268..6f8b1013df 100644 --- a/usr/src/tools/smatch/src/smatch_param_filter.c +++ b/usr/src/tools/smatch/src/smatch_param_filter.c @@ -66,29 +66,28 @@ static struct smatch_state *unmatched_state(struct sm_state *sm) if (parent_is_gone_var_sym(sm->name, sm->sym)) return alloc_estate_empty(); - state = get_state(SMATCH_EXTRA, sm->name, sm->sym); + state = __get_state(SMATCH_EXTRA, sm->name, sm->sym); if (state) return state; return alloc_estate_whole(estate_type(sm->state)); } -static void pre_merge_hook(struct sm_state *sm) +static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) { - struct smatch_state *extra, *mine; + struct smatch_state *extra; struct range_list *rl; - if (estate_rl(sm->state)) + if (estate_rl(other->state)) return; - extra = get_state(SMATCH_EXTRA, sm->name, sm->sym); + extra = get_state(SMATCH_EXTRA, cur->name, cur->sym); if (!extra) return; - mine = get_state(my_id, sm->name, sm->sym); - rl = rl_intersection(estate_rl(extra), estate_rl(mine)); - if (rl_equiv(rl, estate_rl(mine))) + rl = rl_intersection(estate_rl(extra), estate_rl(cur->state)); + if (rl_equiv(rl, estate_rl(cur->state))) return; - set_state(my_id, sm->name, sm->sym, alloc_estate_rl(clone_rl(rl))); + set_state(my_id, cur->name, cur->sym, alloc_estate_rl(clone_rl(rl))); } static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) diff --git a/usr/src/tools/smatch/src/smatch_param_limit.c b/usr/src/tools/smatch/src/smatch_param_limit.c index 3ef8afc3a7..0f626cbd9e 100644 --- a/usr/src/tools/smatch/src/smatch_param_limit.c +++ b/usr/src/tools/smatch/src/smatch_param_limit.c @@ -66,7 +66,7 @@ static struct smatch_state *unmatched_state(struct sm_state *sm) { struct smatch_state *state; - state = get_state(SMATCH_EXTRA, sm->name, sm->sym); + state = __get_state(SMATCH_EXTRA, sm->name, sm->sym); if (state) return state; return alloc_estate_whole(estate_type(sm->state)); @@ -116,14 +116,13 @@ static struct range_list *generify_mtag_range(struct smatch_state *state) * pointer and it's like, "Sorry bro, that's not possible." * */ - rl = rl_intersection(estate_rl(state), valid_ptr_rl); - if (!rl) - return estate_rl(state); - + rl = estate_rl(state); FOR_EACH_PTR(rl, drange) { if (drange->min.value != drange->max.value) continue; - if (drange->min.value > -4096 && drange->min.value <= 0) + if (drange->min.value == 0) + continue; + if (is_err_ptr(drange->min)) continue; return rl_union(valid_ptr_rl, rl); } END_FOR_EACH_PTR(drange); diff --git a/usr/src/tools/smatch/src/smatch_param_set.c b/usr/src/tools/smatch/src/smatch_param_set.c index 44b6e3e659..3752cb04be 100644 --- a/usr/src/tools/smatch/src/smatch_param_set.c +++ b/usr/src/tools/smatch/src/smatch_param_set.c @@ -130,6 +130,24 @@ free: free_string(name); } +static char *get_two_dots(const char *name) +{ + static char buf[80]; + int i, cnt = 0; + + for (i = 0; i < sizeof(buf); i++) { + if (name[i] == '.') { + cnt++; + if (cnt >= 2) { + buf[i] = '\0'; + return buf; + } + } + buf[i] = name[i]; + } + return NULL; +} + /* * This relies on the fact that these states are stored so that * foo->bar is before foo->bar->baz. @@ -154,7 +172,7 @@ static int parent_set(struct string_list *list, const char *name) return 0; } -static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr) +static void print_return_value_param_helper(int return_id, char *return_ranges, struct expression *expr, int limit) { struct sm_state *sm; struct smatch_state *extra; @@ -164,11 +182,13 @@ static void print_return_value_param(int return_id, char *return_ranges, struct struct string_list *set_list = NULL; char *math_str; char buf[256]; + char two_dot[80] = ""; + int count = 0; FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { if (!estate_rl(sm->state)) continue; - extra = get_state(SMATCH_EXTRA, sm->name, sm->sym); + extra = __get_state(SMATCH_EXTRA, sm->name, sm->sym); if (extra) { rl = rl_intersection(estate_rl(sm->state), estate_rl(extra)); if (!rl) @@ -196,6 +216,18 @@ static void print_return_value_param(int return_id, char *return_ranges, struct insert_string(&set_list, (char *)sm->name); continue; } + if (limit) { + char *new = get_two_dots(param_name); + + if (new) { + if (strcmp(new, two_dot) == 0) + continue; + strncpy(two_dot, new, sizeof(two_dot)); + sql_insert_return_states(return_id, return_ranges, + PARAM_SET, param, new, "s64min-s64max"); + continue; + } + } math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym); if (math_str) { @@ -215,27 +247,65 @@ static void print_return_value_param(int return_id, char *return_ranges, struct sql_insert_return_states(return_id, return_ranges, param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET, param, param_name, show_rl(rl)); + if (limit && ++count > limit) + break; } END_FOR_EACH_SM(sm); free_ptr_list((struct ptr_list **)&set_list); } +static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr) +{ + print_return_value_param_helper(return_id, return_ranges, expr, 0); +} + +void print_limited_param_set(int return_id, char *return_ranges, struct expression *expr) +{ + print_return_value_param_helper(return_id, return_ranges, expr, 1000); +} + +static int possibly_empty(struct sm_state *sm) +{ + struct sm_state *tmp; + + FOR_EACH_PTR(sm->possible, tmp) { + if (strcmp(tmp->name, "") == 0) + return 1; + } END_FOR_EACH_PTR(tmp); + return 0; +} + int param_was_set_var_sym(const char *name, struct symbol *sym) { struct sm_state *sm; - int len; + char buf[80]; + int len, i; - FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { - if (sm->sym != sym) + if (!name) + return 0; + + len = strlen(name); + if (len >= sizeof(buf)) + len = sizeof(buf) - 1; + + for (i = 0; i <= len; i++) { + if (name[i] != '-' && name[i] != '\0') continue; - len = strlen(sm->name); - if (strncmp(sm->name, name, len) != 0) + + memcpy(buf, name, i); + buf[i] = '\0'; + + sm = get_sm_state(my_id, buf, sym); + if (!sm) continue; - if (name[len] == '\0' || - name[len] == '-') - return 1; - } END_FOR_EACH_SM(sm); + if (possibly_empty(sm)) + continue; + return 1; + } + + if (name[0] == '*') + return param_was_set_var_sym(name + 1, sym); return 0; } diff --git a/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c b/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c index a4b000f647..cd50dc7554 100644 --- a/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c +++ b/usr/src/tools/smatch/src/smatch_param_to_mtag_data.c @@ -76,18 +76,6 @@ struct smatch_state *merge_tag_info(struct smatch_state *s1, struct smatch_state return &merged; } -static bool is_local_var(struct expression *expr) -{ - struct symbol *sym; - - if (!expr || expr->type != EXPR_SYMBOL) - return false; - sym = expr->symbol; - if (!(sym->ctype.modifiers & MOD_TOPLEVEL)) - return true; - return false; -} - static void match_assign(struct expression *expr) { struct expression *left; @@ -100,7 +88,7 @@ static void match_assign(struct expression *expr) if (expr->op != '=') return; left = strip_expr(expr->left); - if (is_local_var(left)) + if (is_local_variable(left)) return; right_sym = expr_to_sym(expr->right); if (!right_sym) diff --git a/usr/src/tools/smatch/src/smatch_param_used.c b/usr/src/tools/smatch/src/smatch_param_used.c index e81bd8358a..bc2501e990 100644 --- a/usr/src/tools/smatch/src/smatch_param_used.c +++ b/usr/src/tools/smatch/src/smatch_param_used.c @@ -31,12 +31,16 @@ static void get_state_hook(int owner, const char *name, struct symbol *sym) if (!option_info) return; - if (__in_fake_assign || __in_fake_parameter_assign || __in_function_def) + + if (__in_fake_assign || __in_fake_parameter_assign || __in_function_def || __in_unmatched_hook) return; arg = get_param_num_from_sym(sym); - if (arg >= 0) - set_state_stree(&used_stree, my_id, name, sym, &used); + if (arg < 0) + return; + if (param_was_set_var_sym(name, sym)) + return; + set_state_stree(&used_stree, my_id, name, sym, &used); } static void set_param_used(struct expression *call, struct expression *arg, char *key, char *unused) @@ -45,13 +49,19 @@ static void set_param_used(struct expression *call, struct expression *arg, char char *name; int arg_nr; + if (!option_info) + return; + name = get_variable_from_key(arg, key, &sym); if (!name || !sym) goto free; arg_nr = get_param_num_from_sym(sym); - if (arg_nr >= 0) - set_state_stree(&used_stree, my_id, name, sym, &used); + if (arg_nr < 0) + goto free; + if (param_was_set_var_sym(name, sym)) + goto free; + set_state_stree(&used_stree, my_id, name, sym, &used); free: free_string(name); } diff --git a/usr/src/tools/smatch/src/smatch_parse_call_math.c b/usr/src/tools/smatch/src/smatch_parse_call_math.c index 5f3ed679a4..2c3de3158c 100644 --- a/usr/src/tools/smatch/src/smatch_parse_call_math.c +++ b/usr/src/tools/smatch/src/smatch_parse_call_math.c @@ -588,6 +588,9 @@ static char *get_allocation_recipe_from_call(struct expression *expr) BUF_SIZE, sql_filter); if (!buf_size_recipe || strcmp(buf_size_recipe, "invalid") == 0) return NULL; + /* Known sizes should be handled in smatch_buf_size.c */ + if (!strchr(buf_size_recipe, '$')) + return NULL; return swap_format(expr, buf_size_recipe); } @@ -601,26 +604,10 @@ static void match_call_assignment(struct expression *expr) set_state_expr(my_id, expr->left, alloc_state_sname(sname)); } -static void match_returns_call(int return_id, char *return_ranges, struct expression *call) -{ - char *sname; - - sname = get_allocation_recipe_from_call(call); - if (option_debug) - sm_msg("sname = %s", sname); - if (!sname) - return; - - sql_insert_return_states(return_id, return_ranges, BUF_SIZE, -1, "", - sname); -} - -static void print_returned_allocations(int return_id, char *return_ranges, struct expression *expr) +const char *get_allocation_math(struct expression *expr) { struct expression *tmp; struct smatch_state *state; - struct symbol *sym; - char *name; int cnt = 0; expr = strip_expr(expr); @@ -630,25 +617,16 @@ static void print_returned_allocations(int return_id, char *return_ranges, struc expr = strip_expr(tmp); } if (!expr) - return; - - if (expr->type == EXPR_CALL) { - match_returns_call(return_id, return_ranges, expr); - return; - } + return NULL; - name = expr_to_var_sym(expr, &sym); - if (!name || !sym) - goto free; + if (expr->type == EXPR_CALL) + return get_allocation_recipe_from_call(expr); - state = get_state(my_id, name, sym); + state = get_state_expr(my_id, expr); if (!state || !state->data) - goto free; + return NULL; - sql_insert_return_states(return_id, return_ranges, BUF_SIZE, -1, "", - state->name); -free: - free_string(name); + return state->name; } void register_parse_call_math(int id) @@ -663,6 +641,5 @@ void register_parse_call_math(int id) add_function_assign_hook(alloc_functions[i].func, &match_alloc, INT_PTR(alloc_functions[i].param)); add_hook(&match_call_assignment, CALL_ASSIGNMENT_HOOK); - add_split_return_callback(print_returned_allocations); } diff --git a/usr/src/tools/smatch/src/smatch_ranges.c b/usr/src/tools/smatch/src/smatch_ranges.c index d534328476..b30b98b7ff 100644 --- a/usr/src/tools/smatch/src/smatch_ranges.c +++ b/usr/src/tools/smatch/src/smatch_ranges.c @@ -26,7 +26,7 @@ __DO_ALLOCATOR(struct data_range, sizeof(struct data_range), __alignof__(struct "permanent ranges", perm_data_range); __DECLARE_ALLOCATOR(struct ptr_list, rl_ptrlist); -static bool is_err_ptr(sval_t sval) +bool is_err_ptr(sval_t sval) { if (option_project != PROJ_KERNEL) return false; @@ -249,6 +249,13 @@ static int str_to_comparison_arg_helper(const char *str, c++; param = strtoll(c, (char **)&c, 10); + /* + * FIXME: handle parameter math. [==$1 + 100] + * + */ + if (*c == ' ') + return 0; + if (*c == ',' || *c == ']') c++; /* skip the ']' character */ if (endp) @@ -342,6 +349,9 @@ void filter_by_comparison(struct range_list **rl, int comparison, struct range_l struct symbol *cast_type; sval_t min, max; + if (comparison == UNKNOWN_COMPARISON) + return; + cast_type = rl_type(left_orig); if (sval_type_max(rl_type(left_orig)).uvalue < sval_type_max(rl_type(right_orig)).uvalue) cast_type = rl_type(right_orig); @@ -394,6 +404,10 @@ static struct range_list *filter_by_comparison_call(const char *c, struct expres struct range_list *casted_start, *right_orig; int comparison; + /* For when we have a function that takes a function pointer. */ + if (!call || call->type != EXPR_CALL) + return start_rl; + if (!str_to_comparison_arg_helper(c, call, &comparison, &arg, endp)) return start_rl; @@ -491,6 +505,21 @@ static const char *jump_to_call_math(const char *value) return c; } +static struct range_list *get_param_return_rl(struct expression *call, const char *call_math) +{ + struct expression *arg; + int param; + + call_math += 3; + param = atoi(call_math); + + arg = get_argument_from_call_expr(call->args, param); + if (!arg) + return NULL; + + return db_return_vals_no_args(arg); +} + static void str_to_rl_helper(struct expression *call, struct symbol *type, const char *str, const char **endp, struct range_list **rl) { struct range_list *rl_tmp = NULL; @@ -592,6 +621,12 @@ static void str_to_dinfo(struct expression *call, struct symbol *type, const cha goto cast; call_math = jump_to_call_math(value); + if (call_math && call_math[0] == 'r') { + math_rl = get_param_return_rl(call, call_math); + if (math_rl) + rl = rl_intersection(rl, math_rl); + goto cast; + } if (call_math && parse_call_math_rl(call, call_math, &math_rl)) { rl = rl_intersection(rl, math_rl); goto cast; @@ -651,7 +686,7 @@ int is_whole_rl(struct range_list *rl) { struct data_range *drange; - if (ptr_list_empty(rl)) + if (ptr_list_empty((struct ptr_list *)rl)) return 0; drange = first_ptr_list((struct ptr_list *)rl); if (sval_is_min(drange->min) && sval_is_max(drange->max)) @@ -682,7 +717,7 @@ int is_whole_rl_non_zero(struct range_list *rl) { struct data_range *drange; - if (ptr_list_empty(rl)) + if (ptr_list_empty((struct ptr_list *)rl)) return 0; drange = first_ptr_list((struct ptr_list *)rl); if (sval_unsigned(drange->min) && @@ -704,7 +739,7 @@ sval_t rl_min(struct range_list *rl) ret.type = &llong_ctype; ret.value = LLONG_MIN; - if (ptr_list_empty(rl)) + if (ptr_list_empty((struct ptr_list *)rl)) return ret; drange = first_ptr_list((struct ptr_list *)rl); return drange->min; @@ -717,7 +752,7 @@ sval_t rl_max(struct range_list *rl) ret.type = &llong_ctype; ret.value = LLONG_MAX; - if (ptr_list_empty(rl)) + if (ptr_list_empty((struct ptr_list *)rl)) return ret; drange = last_ptr_list((struct ptr_list *)rl); return drange->max; @@ -1190,6 +1225,8 @@ int possibly_true(struct expression *left, int comparison, struct expression *ri struct data_range *tmp_left, *tmp_right; struct symbol *type; + if (comparison == UNKNOWN_COMPARISON) + return 1; if (!get_implied_rl(left, &rl_left)) return 1; if (!get_implied_rl(right, &rl_right)) @@ -1247,7 +1284,7 @@ int possibly_true_rl(struct range_list *left_ranges, int comparison, struct rang struct data_range *left_tmp, *right_tmp; struct symbol *type; - if (!left_ranges || !right_ranges) + if (!left_ranges || !right_ranges || comparison == UNKNOWN_COMPARISON) return 1; type = rl_type(left_ranges); @@ -1273,7 +1310,7 @@ int possibly_false_rl(struct range_list *left_ranges, int comparison, struct ran struct data_range *left_tmp, *right_tmp; struct symbol *type; - if (!left_ranges || !right_ranges) + if (!left_ranges || !right_ranges || comparison == UNKNOWN_COMPARISON) return 1; type = rl_type(left_ranges); @@ -1847,70 +1884,23 @@ static sval_t sval_lowest_set_bit(sval_t sval) return ret; } -static struct range_list *handle_AND_rl_sval(struct range_list *rl, sval_t sval) -{ - struct range_list *known_rl; - sval_t zero = { 0 }; - sval_t min; - - zero.type = sval.type; - zero.value = 0; - - if (sm_fls64(rl_max(rl).uvalue) < find_first_zero_bit(sval.uvalue) && - sm_fls64(rl_min(rl).uvalue) < find_first_zero_bit(sval.uvalue)) - return rl; - - min = sval_lowest_set_bit(sval); - - if (min.value != 0) { - sval_t max, mod; - - max = rl_max(rl); - mod = sval_binop(max, '%', min); - if (mod.value) { - max = sval_binop(max, '-', mod); - max.value++; - if (max.value > 0 && sval_cmp(max, rl_max(rl)) < 0) - rl = remove_range(rl, max, rl_max(rl)); - } - } - - known_rl = alloc_rl(min, sval); - - rl = rl_intersection(rl, known_rl); - zero = rl_min(rl); - zero.value = 0; - add_range(&rl, zero, zero); - - return rl; -} - -static struct range_list *fudge_AND_rl(struct range_list *rl) -{ - struct range_list *ret; - sval_t min; - - min = sval_lowest_set_bit(rl_min(rl)); - ret = clone_rl(rl); - add_range(&ret, min, rl_min(rl)); - - return ret; -} - static struct range_list *handle_AND_rl(struct range_list *left, struct range_list *right) { - sval_t sval, zero; + struct bit_info *one, *two; struct range_list *rl; + sval_t min, max, zero; + unsigned long long bits; + + one = rl_to_binfo(left); + two = rl_to_binfo(right); + bits = one->possible & two->possible; - if (rl_to_sval(left, &sval)) - return handle_AND_rl_sval(right, sval); - if (rl_to_sval(right, &sval)) - return handle_AND_rl_sval(left, sval); + max = rl_max(left); + max.uvalue = bits; + min = sval_lowest_set_bit(max); - left = fudge_AND_rl(left); - right = fudge_AND_rl(right); + rl = alloc_rl(min, max); - rl = rl_intersection(left, right); zero = rl_min(rl); zero.value = 0; add_range(&rl, zero, zero); @@ -2148,7 +2138,6 @@ void split_comparison_rl(struct range_list *left_orig, int op, struct range_list break; default: sm_perror(" unhandled comparison %d", op); - return; } if (left_true_rl) { diff --git a/usr/src/tools/smatch/src/smatch_real_absolute.c b/usr/src/tools/smatch/src/smatch_real_absolute.c index dd6c5581dd..3d98ee6356 100644 --- a/usr/src/tools/smatch/src/smatch_real_absolute.c +++ b/usr/src/tools/smatch/src/smatch_real_absolute.c @@ -36,22 +36,32 @@ static int my_id; -static void pre_merge_hook(struct sm_state *sm) +static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) { struct smatch_state *abs; + struct range_list *rl; + + abs = get_state(my_id, name, sym); + if (!abs || !estate_rl(abs)) + return; + rl = rl_intersection(estate_rl(abs), estate_rl(state)); + set_state(my_id, name, sym, alloc_estate_rl(clone_rl(rl))); +} + +static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) +{ struct smatch_state *extra; struct range_list *rl; - extra = get_state(SMATCH_EXTRA, sm->name, sm->sym); + extra = get_state(SMATCH_EXTRA, cur->name, cur->sym); if (!extra || !estate_rl(extra)) return; - abs = get_state(my_id, sm->name, sm->sym); - if (!abs || !estate_rl(abs)) { - set_state(my_id, sm->name, sm->sym, clone_estate(extra)); + if (!estate_rl(cur->state)) { + set_state(my_id, cur->name, cur->sym, clone_estate(extra)); return; } - rl = rl_intersection(estate_rl(abs), estate_rl(extra)); - set_state(my_id, sm->name, sm->sym, alloc_estate_rl(clone_rl(rl))); + rl = rl_intersection(estate_rl(cur->state), estate_rl(extra)); + set_state(my_id, cur->name, cur->sym, alloc_estate_rl(clone_rl(rl))); } static struct smatch_state *empty_state(struct sm_state *sm) @@ -59,11 +69,6 @@ static struct smatch_state *empty_state(struct sm_state *sm) return alloc_estate_empty(); } -static void reset(struct sm_state *sm, struct expression *mod_expr) -{ - set_state(my_id, sm->name, sm->sym, alloc_estate_whole(estate_type(sm->state))); -} - static int in_iterator_pre_statement(void) { struct statement *stmt; @@ -124,7 +129,7 @@ struct smatch_state *get_real_absolute_state(struct expression *expr) struct smatch_state *get_real_absolute_state_var_sym(const char *name, struct symbol *sym) { - return get_state(my_id, name, sym); + return __get_state(my_id, name, sym); } void register_real_absolute(int id) @@ -135,7 +140,7 @@ void register_real_absolute(int id) add_pre_merge_hook(my_id, &pre_merge_hook); add_unmatched_state_hook(my_id, &empty_state); add_merge_hook(my_id, &merge_estates); - add_modification_hook(my_id, &reset); + add_extra_mod_hook(&extra_mod_hook); add_hook(&match_assign, ASSIGNMENT_HOOK); } diff --git a/usr/src/tools/smatch/src/smatch_returns.c b/usr/src/tools/smatch/src/smatch_returns.c index e04406f225..973dee5f31 100644 --- a/usr/src/tools/smatch/src/smatch_returns.c +++ b/usr/src/tools/smatch/src/smatch_returns.c @@ -59,12 +59,13 @@ void all_return_states_hook(void (*callback)(void)) static void call_hooks(void) { struct return_states_callback *rs_cb; + struct stree *orig; - __set_fake_cur_stree_fast(all_return_states); + orig = __swap_cur_stree(all_return_states); FOR_EACH_PTR(callback_list, rs_cb) { rs_cb->callback(); } END_FOR_EACH_PTR(rs_cb); - __pop_fake_cur_stree_fast(); + __swap_cur_stree(orig); } static void match_return(int return_id, char *return_ranges, struct expression *expr) @@ -116,14 +117,8 @@ struct stree_stack *get_all_return_strees(void) static void free_resources(struct symbol *sym) { - struct stree *tmp; - free_stree(&all_return_states); - - FOR_EACH_PTR(return_stree_stack, tmp) { - free_stree(&tmp); - } END_FOR_EACH_PTR(tmp); - free_stree_stack(&return_stree_stack); + free_stack_and_strees(&return_stree_stack); } void register_returns_early(int id) diff --git a/usr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh b/usr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh index 27ad013bf5..c8f98d9bdb 100755 --- a/usr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh +++ b/usr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh @@ -52,7 +52,14 @@ if [ ! -e smatch_db.sqlite ] ; then fi fi -make -j${NR_CPU} CHECK="$BIN_DIR/smatch --call-tree --info --param-mapper --spammy --file-output" $TARGET +if [[ ! -z $ARCH ]]; then + KERNEL_ARCH="ARCH=$ARCH" +fi +if [[ ! -z $CROSS_COMPILE ]] ; then + KERNEL_CROSS_COMPILE="CROSS_COMPILE=$CROSS_COMPILE" +fi + +make $KERNEL_ARCH $KERNEL_CROSS_COMPILE -j${NR_CPU} CHECK="$BIN_DIR/smatch --call-tree --info --param-mapper --spammy --file-output" $TARGET find -name \*.c.smatch -exec cat \{\} \; -exec rm \{\} \; > smatch_warns.txt diff --git a/usr/src/tools/smatch/src/smatch_scripts/gen_dma_funcs.sh b/usr/src/tools/smatch/src/smatch_scripts/gen_dma_funcs.sh index 6307061b48..7346963b85 100755 --- a/usr/src/tools/smatch/src/smatch_scripts/gen_dma_funcs.sh +++ b/usr/src/tools/smatch/src/smatch_scripts/gen_dma_funcs.sh @@ -22,6 +22,7 @@ echo "// list of DMA function and buffer parameters." > $outfile echo '// generated by `gen_dma_funcs.sh`' >> $outfile ${bin_dir}/trace_params.pl $file usb_control_msg 6 >> $tmp ${bin_dir}/trace_params.pl $file usb_fill_bulk_urb 3 >> $tmp +${bin_dir}/trace_params.pl $file dma_map_single 1 >> $tmp cat $tmp | sort -u > $tmp2 mv $tmp2 $tmp cat $tmp $remove $remove 2> /dev/null | sort | uniq -u >> $outfile diff --git a/usr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh b/usr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh index 338da9d5dd..e1dc44f271 100755 --- a/usr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh +++ b/usr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh @@ -21,7 +21,11 @@ tmp2=$(mktemp /tmp/smatch.XXXX) echo "// list of copy_to_user function and buffer parameters." > $outfile echo '// generated by `gen_rosenberg_funcs.sh`' >> $outfile ${bin_dir}/trace_params.pl $file copy_to_user 1 >> $tmp +${bin_dir}/trace_params.pl $file rds_info_copy 1 >> $tmp ${bin_dir}/trace_params.pl $file nla_put 3 >> $tmp +${bin_dir}/trace_params.pl $file snd_timer_user_append_to_tqueue 1 >> $tmp +${bin_dir}/trace_params.pl $file __send_signal 1 >> $tmp + cat $tmp | sort -u > $tmp2 mv $tmp2 $tmp cat $tmp $remove $remove 2> /dev/null | sort | uniq -u >> $outfile diff --git a/usr/src/tools/smatch/src/smatch_scripts/kchecker b/usr/src/tools/smatch/src/smatch_scripts/kchecker index 9754b21f93..229250b4da 100755 --- a/usr/src/tools/smatch/src/smatch_scripts/kchecker +++ b/usr/src/tools/smatch/src/smatch_scripts/kchecker @@ -70,4 +70,11 @@ if echo $oname | grep -q .o$ ; then rm -f $oname fi -make C=2 $ENDIAN CHECK="$PRE $CMD $POST" $oname +if [[ ! -z $ARCH ]]; then + KERNEL_ARCH="ARCH=$ARCH" +fi +if [[ ! -z $CROSS_COMPILE ]] ; then + KERNEL_CROSS_COMPILE="CROSS_COMPILE=$CROSS_COMPILE" +fi + +make $KERNEL_CROSS_COMPILE $KERNEL_ARCH C=2 $ENDIAN CHECK="$PRE $CMD $POST" $oname diff --git a/usr/src/tools/smatch/src/smatch_scripts/summarize_errs.sh b/usr/src/tools/smatch/src/smatch_scripts/summarize_errs.sh index 5b9a66eb5b..58cf82201f 100755 --- a/usr/src/tools/smatch/src/smatch_scripts/summarize_errs.sh +++ b/usr/src/tools/smatch/src/smatch_scripts/summarize_errs.sh @@ -28,7 +28,7 @@ save_thoughts() echo -n "What do you think?: " read ans if echo $ans | grep ^$ > /dev/null ; then - continue + return fi #store the result diff --git a/usr/src/tools/smatch/src/smatch_scripts/test_generic.sh b/usr/src/tools/smatch/src/smatch_scripts/test_generic.sh index 4655ba7493..5707d7c6ae 100755 --- a/usr/src/tools/smatch/src/smatch_scripts/test_generic.sh +++ b/usr/src/tools/smatch/src/smatch_scripts/test_generic.sh @@ -52,9 +52,16 @@ else exit 1 fi -make clean +if [[ ! -z $ARCH ]]; then + KERNEL_ARCH="ARCH=$ARCH" +fi +if [[ ! -z $CROSS_COMPILE ]] ; then + KERNEL_CROSS_COMPILE="CROSS_COMPILE=$CROSS_COMPILE" +fi + +make $KERNEL_ARCH $KERNEL_CROSS_COMPILE clean find -name \*.c.smatch -exec rm \{\} \; -make -j${NR_CPU} $ENDIAN -k CHECK="$CMD --file-output $*" \ +make $KERNEL_ARCH $KERNEL_CROSS_COMPILE -j${NR_CPU} $ENDIAN -k CHECK="$CMD --file-output $*" \ C=1 $TARGET 2>&1 | tee $LOG find -name \*.c.smatch -exec cat \{\} \; -exec rm \{\} \; > $WLOG diff --git a/usr/src/tools/smatch/src/smatch_scripts/test_kernel.sh b/usr/src/tools/smatch/src/smatch_scripts/test_kernel.sh index b31c61047c..d254c56a63 100755 --- a/usr/src/tools/smatch/src/smatch_scripts/test_kernel.sh +++ b/usr/src/tools/smatch/src/smatch_scripts/test_kernel.sh @@ -56,9 +56,16 @@ else exit 1 fi -make clean +if [[ ! -z $ARCH ]]; then + KERNEL_ARCH="ARCH=$ARCH" +fi +if [[ ! -z $CROSS_COMPILE ]] ; then + KERNEL_CROSS_COMPILE="CROSS_COMPILE=$CROSS_COMPILE" +fi + +make $KERNEL_ARCH $KERNEL_CROSS_COMPILE clean find -name \*.c.smatch -exec rm \{\} \; -make -j${NR_CPU} $ENDIAN -k CHECK="$CMD -p=kernel --file-output --succeed $*" \ +make $KERNEL_ARCH $KERNEL_CROSS_COMPILE -j${NR_CPU} $ENDIAN -k CHECK="$CMD -p=kernel --file-output --succeed $*" \ C=1 $BUILD_PARAM $TARGET 2>&1 | tee $LOG BUILD_STATUS=${PIPESTATUS[0]} find -name \*.c.smatch -exec cat \{\} \; -exec rm \{\} \; > $WLOG diff --git a/usr/src/tools/smatch/src/smatch_slist.c b/usr/src/tools/smatch/src/smatch_slist.c index ef00465593..5c20dd739c 100644 --- a/usr/src/tools/smatch/src/smatch_slist.c +++ b/usr/src/tools/smatch/src/smatch_slist.c @@ -41,8 +41,9 @@ const char *show_sm(struct sm_state *sm) if (!sm) return "<none>"; - pos = snprintf(buf, sizeof(buf), "[%s] '%s' = '%s'", - check_name(sm->owner), sm->name, show_state(sm->state)); + pos = snprintf(buf, sizeof(buf), "[%s] %s = '%s'%s", + check_name(sm->owner), sm->name, show_state(sm->state), + sm->merged ? " [merged]" : ""); if (pos > sizeof(buf)) goto truncate; @@ -691,6 +692,8 @@ static void match_states_stree(struct stree **one, struct stree **two) AvlIter one_iter; AvlIter two_iter; + __set_cur_stree_readonly(); + avl_iter_begin(&one_iter, *one, FORWARD); avl_iter_begin(&two_iter, *two, FORWARD); @@ -699,7 +702,9 @@ static void match_states_stree(struct stree **one, struct stree **two) break; if (cmp_tracker(one_iter.sm, two_iter.sm) < 0) { __set_fake_cur_stree_fast(*two); + __in_unmatched_hook++; tmp_state = __client_unmatched_state_function(one_iter.sm); + __in_unmatched_hook--; __pop_fake_cur_stree_fast(); sm = alloc_state_no_name(one_iter.sm->owner, one_iter.sm->name, one_iter.sm->sym, tmp_state); @@ -710,7 +715,9 @@ static void match_states_stree(struct stree **one, struct stree **two) avl_iter_next(&two_iter); } else { __set_fake_cur_stree_fast(*one); + __in_unmatched_hook++; tmp_state = __client_unmatched_state_function(two_iter.sm); + __in_unmatched_hook--; __pop_fake_cur_stree_fast(); sm = alloc_state_no_name(two_iter.sm->owner, two_iter.sm->name, two_iter.sm->sym, tmp_state); @@ -719,6 +726,8 @@ static void match_states_stree(struct stree **one, struct stree **two) } } + __set_cur_stree_writable(); + FOR_EACH_PTR(add_to_one, sm) { avl_insert(one, sm); } END_FOR_EACH_PTR(sm); @@ -733,29 +742,38 @@ static void match_states_stree(struct stree **one, struct stree **two) static void call_pre_merge_hooks(struct stree **one, struct stree **two) { - struct sm_state *sm, *other; + struct sm_state *sm, *cur; + struct stree *new; - save_all_states(); + __in_unmatched_hook++; - __swap_cur_stree(*one); + __set_fake_cur_stree_fast(*one); + __push_fake_cur_stree(); FOR_EACH_SM(*two, sm) { - other = get_sm_state(sm->owner, sm->name, sm->sym); - if (other == sm) + cur = get_sm_state(sm->owner, sm->name, sm->sym); + if (cur == sm) continue; - call_pre_merge_hook(sm); + call_pre_merge_hook(cur, sm); } END_FOR_EACH_SM(sm); - *one = clone_stree(__get_cur_stree()); + new = __pop_fake_cur_stree(); + overwrite_stree(new, one); + free_stree(&new); + __pop_fake_cur_stree_fast(); - __swap_cur_stree(*two); + __set_fake_cur_stree_fast(*two); + __push_fake_cur_stree(); FOR_EACH_SM(*one, sm) { - other = get_sm_state(sm->owner, sm->name, sm->sym); - if (other == sm) + cur = get_sm_state(sm->owner, sm->name, sm->sym); + if (cur == sm) continue; - call_pre_merge_hook(sm); + call_pre_merge_hook(cur, sm); } END_FOR_EACH_SM(sm); - *two = clone_stree(__get_cur_stree()); + new = __pop_fake_cur_stree(); + overwrite_stree(new, two); + free_stree(&new); + __pop_fake_cur_stree_fast(); - restore_all_states(); + __in_unmatched_hook--; } static void clone_pool_havers_stree(struct stree **stree) diff --git a/usr/src/tools/smatch/src/smatch_states.c b/usr/src/tools/smatch/src/smatch_states.c index d3656dff5a..013405f638 100644 --- a/usr/src/tools/smatch/src/smatch_states.c +++ b/usr/src/tools/smatch/src/smatch_states.c @@ -44,6 +44,7 @@ struct smatch_state true_state = { .name = "true" }; struct smatch_state false_state = { .name = "false" }; static struct stree *cur_stree; /* current states */ +static struct stree *fast_overlay; static struct stree_stack *true_stack; /* states after a t/f branch */ static struct stree_stack *false_stack; @@ -80,6 +81,16 @@ int unreachable(void) return 0; } +void __set_cur_stree_readonly(void) +{ + read_only++; +} + +void __set_cur_stree_writable(void) +{ + read_only--; +} + struct sm_state *set_state(int owner, const char *name, struct symbol *sym, struct smatch_state *state) { struct sm_state *ret; @@ -130,10 +141,12 @@ free: return ret; } -void __swap_cur_stree(struct stree *stree) +struct stree *__swap_cur_stree(struct stree *stree) { - free_stree(&cur_stree); + struct stree *orig = cur_stree; + cur_stree = stree; + return orig; } void __push_fake_cur_stree(void) @@ -160,15 +173,18 @@ void __free_fake_cur_stree(void) void __set_fake_cur_stree_fast(struct stree *stree) { - push_stree(&pre_cond_stack, cur_stree); - cur_stree = stree; - read_only = 1; + if (fast_overlay) { + sm_perror("cannot nest fast overlay"); + return; + } + fast_overlay = stree; + set_fast_math_only(); } void __pop_fake_cur_stree_fast(void) { - cur_stree = pop_stree(&pre_cond_stack); - read_only = 0; + fast_overlay = NULL; + clear_fast_math_only(); } void __merge_stree_into_cur(struct stree *stree) @@ -289,7 +305,12 @@ static void call_get_state_hooks(int owner, const char *name, struct symbol *sym struct smatch_state *__get_state(int owner, const char *name, struct symbol *sym) { - return get_state_stree(cur_stree, owner, name, sym); + struct sm_state *sm; + + sm = get_sm_state(owner, name, sym); + if (!sm) + return NULL; + return sm->state; } struct smatch_state *get_state(int owner, const char *name, struct symbol *sym) @@ -343,6 +364,12 @@ free: struct sm_state *get_sm_state(int owner, const char *name, struct symbol *sym) { + struct sm_state *ret; + + ret = get_sm_state_stree(fast_overlay, owner, name, sym); + if (ret) + return ret; + return get_sm_state_stree(cur_stree, owner, name, sym); } @@ -593,39 +620,39 @@ static void check_stree_stack_free(struct stree_stack **stack) void save_all_states(void) { - __add_ptr_list(&backup, cur_stree, 0); + __add_ptr_list(&backup, cur_stree); cur_stree = NULL; - __add_ptr_list(&backup, true_stack, 0); + __add_ptr_list(&backup, true_stack); true_stack = NULL; - __add_ptr_list(&backup, false_stack, 0); + __add_ptr_list(&backup, false_stack); false_stack = NULL; - __add_ptr_list(&backup, pre_cond_stack, 0); + __add_ptr_list(&backup, pre_cond_stack); pre_cond_stack = NULL; - __add_ptr_list(&backup, cond_true_stack, 0); + __add_ptr_list(&backup, cond_true_stack); cond_true_stack = NULL; - __add_ptr_list(&backup, cond_false_stack, 0); + __add_ptr_list(&backup, cond_false_stack); cond_false_stack = NULL; - __add_ptr_list(&backup, fake_cur_stree_stack, 0); + __add_ptr_list(&backup, fake_cur_stree_stack); fake_cur_stree_stack = NULL; - __add_ptr_list(&backup, break_stack, 0); + __add_ptr_list(&backup, break_stack); break_stack = NULL; - __add_ptr_list(&backup, fake_break_stack, 0); + __add_ptr_list(&backup, fake_break_stack); fake_break_stack = NULL; - __add_ptr_list(&backup, switch_stack, 0); + __add_ptr_list(&backup, switch_stack); switch_stack = NULL; - __add_ptr_list(&backup, remaining_cases, 0); + __add_ptr_list(&backup, remaining_cases); remaining_cases = NULL; - __add_ptr_list(&backup, default_stack, 0); + __add_ptr_list(&backup, default_stack); default_stack = NULL; - __add_ptr_list(&backup, continue_stack, 0); + __add_ptr_list(&backup, continue_stack); continue_stack = NULL; - __add_ptr_list(&backup, goto_stack, 0); + __add_ptr_list(&backup, goto_stack); goto_stack = NULL; } diff --git a/usr/src/tools/smatch/src/smatch_struct_assignment.c b/usr/src/tools/smatch/src/smatch_struct_assignment.c index 7fcd631e3e..2670eca445 100644 --- a/usr/src/tools/smatch/src/smatch_struct_assignment.c +++ b/usr/src/tools/smatch/src/smatch_struct_assignment.c @@ -167,7 +167,8 @@ static void handle_non_struct_assignments(struct expression *left, struct expres if (type->type != SYM_BASETYPE) return; right = strip_expr(right); - if (!right) + type = get_type(right); + if (!right || !type || type->type == SYM_ARRAY) right = unknown_value_expression(left); assign = assign_expression(left, '=', right); split_fake_expr(assign); @@ -404,7 +405,7 @@ void __fake_struct_member_assignments(struct expression *expr) if (expr->op != '=') return; - if (is_zero(expr->right)) + if (expr_is_zero(expr->right)) return; left_type = get_type(expr->left); diff --git a/usr/src/tools/smatch/src/smatch_type.c b/usr/src/tools/smatch/src/smatch_type.c index fca1fb8bac..305a0b5c33 100644 --- a/usr/src/tools/smatch/src/smatch_type.c +++ b/usr/src/tools/smatch/src/smatch_type.c @@ -398,7 +398,7 @@ int returns_pointer(struct symbol *sym) if (!sym || sym->type != SYM_FN) return 0; sym = get_base_type(sym); - if (sym->type == SYM_PTR) + if (sym && sym->type == SYM_PTR) return 1; return 0; } @@ -496,20 +496,16 @@ free: return ret; } -int is_local_variable(struct expression *expr) +bool is_local_variable(struct expression *expr) { struct symbol *sym; - char *name; - name = expr_to_var_sym(expr, &sym); - free_string(name); - if (!sym || !sym->scope || !sym->scope->token || !cur_func_sym) - return 0; - if (cmp_pos(sym->scope->token->pos, cur_func_sym->pos) < 0) - return 0; - if (is_static(expr)) - return 0; - return 1; + if (!expr || expr->type != EXPR_SYMBOL || !expr->symbol) + return false; + sym = expr->symbol; + if (!(sym->ctype.modifiers & MOD_TOPLEVEL)) + return true; + return false; } int types_equiv(struct symbol *one, struct symbol *two) diff --git a/usr/src/tools/smatch/src/smatch_type_val.c b/usr/src/tools/smatch/src/smatch_type_val.c index 89be79e679..16445eccdb 100644 --- a/usr/src/tools/smatch/src/smatch_type_val.c +++ b/usr/src/tools/smatch/src/smatch_type_val.c @@ -227,13 +227,46 @@ static int is_container_of(void) return 1; } -static int is_ignored_macro(void) +static bool is_driver_data(void) { + static struct expression *prev_expr; struct expression *expr; char *name; + static bool prev_ret; + bool ret = false; expr = get_faked_expression(); if (!expr || expr->type != EXPR_ASSIGNMENT) + return false; + + if (expr == prev_expr) + return prev_ret; + prev_expr = expr; + + name = expr_to_str(expr->right); + if (!name) { + prev_ret = false; + return false; + } + + if (strstr(name, "get_drvdata(") || + strstr(name, "dev.driver_data") || + strstr(name, "dev->driver_data")) + ret = true; + + free_string(name); + + prev_ret = ret; + return ret; +} + +static int is_ignored_macro(void) +{ + struct expression *expr; + char *name; + + expr = get_faked_expression(); + if (!expr || expr->type != EXPR_ASSIGNMENT || expr->op != '=') return 0; name = get_macro_name(expr->right->pos); if (!name) @@ -248,6 +281,20 @@ static int is_ignored_macro(void) return 1; if (strcmp(name, "hlist_entry") == 0) return 1; + if (strcmp(name, "per_cpu_ptr") == 0) + return 1; + if (strcmp(name, "raw_cpu_ptr") == 0) + return 1; + if (strcmp(name, "this_cpu_ptr") == 0) + return 1; + + if (strcmp(name, "TRACE_EVENT") == 0) + return 1; + if (strcmp(name, "DECLARE_EVENT_CLASS") == 0) + return 1; + if (strcmp(name, "DEFINE_EVENT") == 0) + return 1; + if (strstr(name, "for_each")) return 1; return 0; @@ -266,10 +313,30 @@ static int is_ignored_function(void) if (sym_name_is("kmalloc", expr->fn)) return 1; + if (sym_name_is("vmalloc", expr->fn)) + return 1; + if (sym_name_is("kvmalloc", expr->fn)) + return 1; + if (sym_name_is("kmalloc_array", expr->fn)) + return 1; + if (sym_name_is("vmalloc_array", expr->fn)) + return 1; + if (sym_name_is("kvmalloc_array", expr->fn)) + return 1; + + if (sym_name_is("mmu_memory_cache_alloc", expr->fn)) + return 1; + if (sym_name_is("kmem_alloc", expr->fn)) + return 1; + if (sym_name_is("alloc_pages", expr->fn)) + return 1; + if (sym_name_is("netdev_priv", expr->fn)) return 1; if (sym_name_is("dev_get_drvdata", expr->fn)) return 1; + if (sym_name_is("i2c_get_clientdata", expr->fn)) + return 1; return 0; } @@ -294,6 +361,9 @@ static int is_uncasted_pointer_assign(void) if (!left_type || !right_type) return 0; + if (left_type->type == SYM_STRUCT && left_type == right_type) + return 1; + if (left_type->type != SYM_PTR && left_type->type != SYM_ARRAY) return 0; @@ -396,6 +466,8 @@ static void match_assign_value(struct expression *expr) return; type = get_type(expr->left); + if (type && type->type == SYM_STRUCT) + return; member = get_member_name(expr->left); if (!member) return; @@ -416,6 +488,8 @@ static void match_assign_value(struct expression *expr) goto free; if (is_container_of()) goto free; + if (is_driver_data()) + goto free; add_fake_type_val(member, alloc_whole_rl(get_type(expr->left)), is_ignored_fake_assignment()); goto free; } @@ -501,24 +575,14 @@ static void asm_expr(struct statement *stmt) struct expression *expr; struct range_list *rl; char *member; - int state = 0; FOR_EACH_PTR(stmt->asm_outputs, expr) { - switch (state) { - case 0: /* identifier */ - case 1: /* constraint */ - state++; - continue; - case 2: /* expression */ - state = 0; - member = get_member_name(expr); - if (!member) - continue; - rl = alloc_whole_rl(get_type(expr)); - add_type_val(member, rl); - free_string(member); + member = get_member_name(expr->expr); + if (!member) continue; - } + rl = alloc_whole_rl(get_type(expr->expr)); + add_type_val(member, rl); + free_string(member); } END_FOR_EACH_PTR(expr); } @@ -542,6 +606,19 @@ static void db_param_add(struct expression *expr, int param, char *key, char *va if (!arg) return; type = get_member_type_from_key(arg, key); + /* + * The situation here is that say we memset() a void pointer to zero + * then that's returned to the called as "*$ = 0;" but on the caller's + * side it's not void, it's a struct. + * + * So the question is should we be passing that slightly bogus + * information back to the caller? Maybe, maybe not, but either way we + * are not going to record it here because a struct can't be zero. + * + */ + if (type && type->type == SYM_STRUCT) + return; + if (arg->type != EXPR_PREOP || arg->op != '&') return; arg = strip_expr(arg->unop); diff --git a/usr/src/tools/smatch/src/smatch_untracked_param.c b/usr/src/tools/smatch/src/smatch_untracked_param.c index d6ff0e7010..e2c1707482 100644 --- a/usr/src/tools/smatch/src/smatch_untracked_param.c +++ b/usr/src/tools/smatch/src/smatch_untracked_param.c @@ -268,31 +268,20 @@ static void match_param_assign(struct expression *expr) static void match_param_assign_in_asm(struct statement *stmt) { - struct expression *expr; + struct expression *tmp, *expr; struct symbol *type; - int state = 0; int param; - FOR_EACH_PTR(stmt->asm_inputs, expr) { - switch (state) { - case 0: /* identifier */ - case 1: /* constraint */ - state++; + FOR_EACH_PTR(stmt->asm_inputs, tmp) { + expr = strip_expr(tmp->expr); + type = get_type(expr); + if (!type || type->type != SYM_PTR) continue; - case 2: /* expression */ - state = 0; - - expr = strip_expr(expr); - type = get_type(expr); - if (!type || type->type != SYM_PTR) - continue; - param = get_param_num(expr); - if (param < 0) - continue; - set_state_expr(my_id, expr, &untracked); + param = get_param_num(expr); + if (param < 0) continue; - } - } END_FOR_EACH_PTR(expr); + set_state_expr(my_id, expr, &untracked); + } END_FOR_EACH_PTR(tmp); } static void match_inline_start(struct expression *expr) diff --git a/usr/src/tools/smatch/src/sort.c b/usr/src/tools/smatch/src/sort.c index 430ba44784..101e0f0e41 100644 --- a/usr/src/tools/smatch/src/sort.c +++ b/usr/src/tools/smatch/src/sort.c @@ -138,7 +138,7 @@ merge_block_seqs (struct ptr_list *b1, int n, // Do a quick skip in case entire blocks from b1 are // already less than smallest element in b2. while (b1->nr == 0 || - cmp (PTR_ENTRY(b1, b1->nr - 1), PTR_ENTRY(b2,0)) < 0) { + cmp (PTR_ENTRY_NOTAG(b1, b1->nr - 1), PTR_ENTRY_NOTAG(b2,0)) < 0) { // printf ("Skipping whole block.\n"); BEEN_THERE('H'); b1 = b1->next; @@ -149,8 +149,8 @@ merge_block_seqs (struct ptr_list *b1, int n, } while (1) { - const void *d1 = PTR_ENTRY(b1,i1); - const void *d2 = PTR_ENTRY(b2,i2); + const void *d1 = PTR_ENTRY_NOTAG(b1,i1); + const void *d2 = PTR_ENTRY_NOTAG(b2,i2); assert (i1 >= 0 && i1 < b1->nr); assert (i2 >= 0 && i2 < b2->nr); diff --git a/usr/src/tools/smatch/src/sparse-llvm-dis b/usr/src/tools/smatch/src/sparse-llvm-dis new file mode 100755 index 0000000000..2958217b4a --- /dev/null +++ b/usr/src/tools/smatch/src/sparse-llvm-dis @@ -0,0 +1,15 @@ +#!/bin/sh +# +# For testing sparse-llvm emitted bytecode + +set +e + +DIS=$("${LLVM_CONFIG:-llvm-config}" --bindir)/llvm-dis + +if [ $# -eq 0 ]; then + echo "$(basename $0): no input files" + exit 1 +fi + +DIRNAME=$(dirname $0) +$DIRNAME/sparse-llvm "$@" | "$DIS" | grep -v '^target ' diff --git a/usr/src/tools/smatch/src/sparse-llvm.c b/usr/src/tools/smatch/src/sparse-llvm.c index 31f87f0b9b..c7a9fbb7ee 100644 --- a/usr/src/tools/smatch/src/sparse-llvm.c +++ b/usr/src/tools/smatch/src/sparse-llvm.c @@ -21,62 +21,35 @@ struct function { LLVMBuilderRef builder; - LLVMTypeRef type; LLVMValueRef fn; LLVMModuleRef module; }; -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 symbol_type(struct symbol *sym); -static LLVMTypeRef func_return_type(LLVMModuleRef module, struct symbol *sym) +static LLVMTypeRef func_return_type(struct symbol *sym) { - return symbol_type(module, sym->ctype.base_type); + return symbol_type(sym->ctype.base_type); } -static LLVMTypeRef sym_func_type(LLVMModuleRef module, struct symbol *sym) +static LLVMTypeRef sym_func_type(struct symbol *sym) { - LLVMTypeRef *arg_type; - LLVMTypeRef func_type; - LLVMTypeRef ret_type; + int n_arg = symbol_list_size(sym->arguments); + LLVMTypeRef *arg_type = calloc(n_arg, sizeof(LLVMTypeRef)); + LLVMTypeRef ret_type = func_return_type(sym); 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); + arg_type[idx++] = symbol_type(arg_sym); } END_FOR_EACH_PTR(arg); - func_type = LLVMFunctionType(ret_type, arg_type, n_arg, - sym->variadic); - return func_type; + return LLVMFunctionType(ret_type, arg_type, n_arg, sym->variadic); } -static LLVMTypeRef sym_array_type(LLVMModuleRef module, struct symbol *sym) +static LLVMTypeRef sym_array_type(struct symbol *sym) { LLVMTypeRef elem_type; struct symbol *base_type; @@ -85,7 +58,7 @@ static LLVMTypeRef sym_array_type(LLVMModuleRef module, struct symbol *sym) /* empty struct is undefined [6.7.2.1(8)] */ assert(base_type->bit_size > 0); - elem_type = symbol_type(module, base_type); + elem_type = symbol_type(base_type); if (!elem_type) return NULL; @@ -94,7 +67,7 @@ static LLVMTypeRef sym_array_type(LLVMModuleRef module, struct symbol *sym) #define MAX_STRUCT_MEMBERS 64 -static LLVMTypeRef sym_struct_type(LLVMModuleRef module, struct symbol *sym) +static LLVMTypeRef sym_struct_type(struct symbol *sym) { LLVMTypeRef elem_types[MAX_STRUCT_MEMBERS]; struct symbol *member; @@ -112,7 +85,7 @@ static LLVMTypeRef sym_struct_type(LLVMModuleRef module, struct symbol *sym) assert(nr < MAX_STRUCT_MEMBERS); - member_type = symbol_type(module, member); + member_type = symbol_type(member); elem_types[nr++] = member_type; } END_FOR_EACH_PTR(member); @@ -121,7 +94,7 @@ static LLVMTypeRef sym_struct_type(LLVMModuleRef module, struct symbol *sym) return ret; } -static LLVMTypeRef sym_union_type(LLVMModuleRef module, struct symbol *sym) +static LLVMTypeRef sym_union_type(struct symbol *sym) { LLVMTypeRef elements; unsigned union_size; @@ -138,7 +111,7 @@ static LLVMTypeRef sym_union_type(LLVMModuleRef module, struct symbol *sym) return LLVMStructType(&elements, 1, 0 /* packed? */); } -static LLVMTypeRef sym_ptr_type(LLVMModuleRef module, struct symbol *sym) +static LLVMTypeRef sym_ptr_type(struct symbol *sym) { LLVMTypeRef type; @@ -146,7 +119,7 @@ static LLVMTypeRef sym_ptr_type(LLVMModuleRef module, struct symbol *sym) if (is_void_type(sym->ctype.base_type)) type = LLVMInt8Type(); else - type = symbol_type(module, sym->ctype.base_type); + type = symbol_type(sym->ctype.base_type); return LLVMPointerType(type, 0); } @@ -155,7 +128,7 @@ static LLVMTypeRef sym_basetype_type(struct symbol *sym) { LLVMTypeRef ret = NULL; - if (symbol_is_fp_type(sym)) { + if (is_float_type(sym)) { switch (sym->bit_size) { case 32: ret = LLVMFloatType(); @@ -199,39 +172,42 @@ static LLVMTypeRef sym_basetype_type(struct symbol *sym) return ret; } -static LLVMTypeRef symbol_type(LLVMModuleRef module, struct symbol *sym) +static LLVMTypeRef symbol_type(struct symbol *sym) { LLVMTypeRef ret = NULL; /* don't cache the result for SYM_NODE */ if (sym->type == SYM_NODE) - return symbol_type(module, sym->ctype.base_type); + return symbol_type(sym->ctype.base_type); if (sym->aux) return sym->aux; switch (sym->type) { case SYM_BITFIELD: + ret = LLVMIntType(sym->bit_size); + break; + case SYM_RESTRICT: case SYM_ENUM: - ret = symbol_type(module, sym->ctype.base_type); + ret = symbol_type(sym->ctype.base_type); break; case SYM_BASETYPE: ret = sym_basetype_type(sym); break; case SYM_PTR: - ret = sym_ptr_type(module, sym); + ret = sym_ptr_type(sym); break; case SYM_UNION: - ret = sym_union_type(module, sym); + ret = sym_union_type(sym); break; case SYM_STRUCT: - ret = sym_struct_type(module, sym); + ret = sym_struct_type(sym); break; case SYM_ARRAY: - ret = sym_array_type(module, sym); + ret = sym_array_type(sym); break; case SYM_FN: - ret = sym_func_type(module, sym); + ret = sym_func_type(sym); break; default: assert(0); @@ -242,28 +218,23 @@ static LLVMTypeRef symbol_type(LLVMModuleRef module, struct symbol *sym) return ret; } -static LLVMTypeRef int_type_by_size(int size) +static LLVMTypeRef insn_symbol_type(struct instruction *insn) { - switch (size) { - case 1: return LLVMInt1Type(); + if (insn->type) + return symbol_type(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", size); + die("invalid bit size %d", insn->size); break; } - return NULL; /* not reached */ -} -static LLVMTypeRef insn_symbol_type(LLVMModuleRef module, struct instruction *insn) -{ - if (insn->type) - return symbol_type(module, insn->type); - - return int_type_by_size(insn->size); + return NULL; /* not reached */ } static LLVMLinkage data_linkage(struct symbol *sym) @@ -284,31 +255,118 @@ static LLVMLinkage function_linkage(struct symbol *sym) #define MAX_PSEUDO_NAME 64 -static void pseudo_name(pseudo_t pseudo, char *buf) +static const char *pseudo_name(pseudo_t pseudo, char *buf) { switch (pseudo->type) { case PSEUDO_REG: - snprintf(buf, MAX_PSEUDO_NAME, "R%d", pseudo->nr); + snprintf(buf, MAX_PSEUDO_NAME, "R%d.", pseudo->nr); break; - case PSEUDO_SYM: - assert(0); + case PSEUDO_PHI: + snprintf(buf, MAX_PSEUDO_NAME, "PHI%d.", pseudo->nr); break; + case PSEUDO_SYM: case PSEUDO_VAL: - assert(0); + case PSEUDO_ARG: + case PSEUDO_VOID: + buf[0] = '\0'; break; - case PSEUDO_ARG: { + case PSEUDO_UNDEF: assert(0); break; + default: + assert(0); } - case PSEUDO_PHI: - snprintf(buf, MAX_PSEUDO_NAME, "PHI%d", pseudo->nr); + + return buf; +} + +static LLVMValueRef get_sym_value(LLVMModuleRef module, struct symbol *sym) +{ + const char *name = show_ident(sym->ident); + LLVMTypeRef type = symbol_type(sym); + LLVMValueRef result = NULL; + struct expression *expr; + + assert(sym->bb_target == NULL); + + expr = sym->initializer; + if (expr && !sym->ident) { + 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(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)); + return result; + } + default: + break; + } + } + + if (LLVMGetTypeKind(type) == LLVMFunctionTypeKind) { + result = LLVMGetNamedFunction(module, name); + if (!result) + result = LLVMAddFunction(module, name, type); + } else { + result = LLVMGetNamedGlobal(module, name); + if (!result) + result = LLVMAddGlobal(module, type, name); + } + + return result; +} + +static LLVMValueRef constant_value(unsigned long long val, LLVMTypeRef dtype) +{ + LLVMValueRef result; + + switch (LLVMGetTypeKind(dtype)) { + case LLVMPointerTypeKind: + if (val != 0) { // for example: ... = (void*) 0x123; + LLVMTypeRef itype = LLVMIntType(bits_in_pointer); + result = LLVMConstInt(itype, val, 1); + result = LLVMConstIntToPtr(result, dtype); + } else { + result = LLVMConstPointerNull(dtype); + } + break; + case LLVMIntegerTypeKind: + result = LLVMConstInt(dtype, val, 1); + break; + case LLVMArrayTypeKind: + case LLVMStructTypeKind: + if (val != 0) + return NULL; + result = LLVMConstNull(dtype); break; default: - assert(0); + return NULL; } + return result; +} + +static LLVMValueRef val_to_value(unsigned long long val, struct symbol *ctype) +{ + LLVMValueRef result; + LLVMTypeRef dtype; + + assert(ctype); + dtype = symbol_type(ctype); + result = constant_value(val, dtype); + if (result) + return result; + sparse_error(ctype->pos, "no value possible for %s", show_typename(ctype)); + return LLVMGetUndef(symbol_type(ctype)); } -static LLVMValueRef pseudo_to_value(struct function *fn, struct instruction *insn, pseudo_t pseudo) +static LLVMValueRef pseudo_to_value(struct function *fn, struct symbol *ctype, pseudo_t pseudo) { LLVMValueRef result = NULL; @@ -316,56 +374,11 @@ static LLVMValueRef pseudo_to_value(struct function *fn, struct instruction *ins case PSEUDO_REG: result = pseudo->priv; break; - case PSEUDO_SYM: { - struct symbol *sym = pseudo->sym; - struct expression *expr; - - assert(sym->bb_target == 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; - } - case EXPR_SYMBOL: { - struct symbol *sym = expr->symbol; - - result = LLVMGetNamedGlobal(fn->module, show_ident(sym->ident)); - assert(result != NULL); - break; - } - default: - assert(0); - } - } else { - const char *name = show_ident(sym->ident); - LLVMTypeRef type = symbol_type(fn->module, sym); - - if (LLVMGetTypeKind(type) == LLVMFunctionTypeKind) { - result = LLVMGetNamedFunction(fn->module, name); - if (!result) - result = LLVMAddFunction(fn->module, name, type); - } else { - result = LLVMGetNamedGlobal(fn->module, name); - if (!result) - result = LLVMAddGlobal(fn->module, type, name); - } - } + case PSEUDO_SYM: + result = get_sym_value(fn->module, pseudo->sym); break; - } case PSEUDO_VAL: - result = LLVMConstInt(int_type_by_size(pseudo->size), pseudo->value, 1); + result = val_to_value(pseudo->value, ctype); break; case PSEUDO_ARG: { result = LLVMGetParam(fn->fn, pseudo->nr - 1); @@ -377,6 +390,9 @@ static LLVMValueRef pseudo_to_value(struct function *fn, struct instruction *ins case PSEUDO_VOID: result = NULL; break; + case PSEUDO_UNDEF: + result = LLVMGetUndef(symbol_type(ctype)); + break; default: assert(0); } @@ -384,36 +400,114 @@ static LLVMValueRef pseudo_to_value(struct function *fn, struct instruction *ins return result; } +static LLVMValueRef pseudo_to_rvalue(struct function *fn, struct symbol *ctype, pseudo_t pseudo) +{ + LLVMValueRef val = pseudo_to_value(fn, ctype, pseudo); + LLVMTypeRef dtype = symbol_type(ctype); + char name[MAX_PSEUDO_NAME]; + + pseudo_name(pseudo, name); + return LLVMBuildBitCast(fn->builder, val, dtype, name); +} + +static LLVMValueRef value_to_ivalue(struct function *fn, struct symbol *ctype, LLVMValueRef val) +{ + const char *name = LLVMGetValueName(val); + LLVMTypeRef dtype = symbol_type(ctype); + + if (LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMPointerTypeKind) { + LLVMTypeRef dtype = LLVMIntType(bits_in_pointer); + val = LLVMBuildPtrToInt(fn->builder, val, dtype, name); + } + if (ctype && is_int_type(ctype)) { + val = LLVMBuildIntCast(fn->builder, val, dtype, name); + } + return val; +} + +static LLVMValueRef value_to_pvalue(struct function *fn, struct symbol *ctype, LLVMValueRef val) +{ + const char *name = LLVMGetValueName(val); + LLVMTypeRef dtype = symbol_type(ctype); + + assert(is_ptr_type(ctype)); + switch (LLVMGetTypeKind(LLVMTypeOf(val))) { + case LLVMIntegerTypeKind: + val = LLVMBuildIntToPtr(fn->builder, val, dtype, name); + break; + case LLVMPointerTypeKind: + val = LLVMBuildBitCast(fn->builder, val, dtype, name); + break; + default: + break; + } + return val; +} + +static LLVMValueRef adjust_type(struct function *fn, struct symbol *ctype, LLVMValueRef val) +{ + if (is_int_type(ctype)) + return value_to_ivalue(fn, ctype, val); + if (is_ptr_type(ctype)) + return value_to_pvalue(fn, ctype, val); + return val; +} + +/* + * Get the LLVMValue corresponding to the pseudo + * and force the type corresponding to ctype. + */ +static LLVMValueRef get_operand(struct function *fn, struct symbol *ctype, pseudo_t pseudo) +{ + LLVMValueRef target = pseudo_to_value(fn, ctype, pseudo); + return adjust_type(fn, ctype, target); +} + +/* + * Get the LLVMValue corresponding to the pseudo + * and force the type corresponding to ctype but + * map all pointers to intptr_t. + */ +static LLVMValueRef get_ioperand(struct function *fn, struct symbol *ctype, pseudo_t pseudo) +{ + LLVMValueRef target = pseudo_to_value(fn, ctype, pseudo); + return value_to_ivalue(fn, ctype, target); +} + static LLVMValueRef calc_gep(LLVMBuilderRef builder, LLVMValueRef base, LLVMValueRef off) { LLVMTypeRef type = LLVMTypeOf(base); unsigned int as = LLVMGetPointerAddressSpace(type); LLVMTypeRef bytep = LLVMPointerType(LLVMInt8Type(), as); LLVMValueRef addr; + const char *name = LLVMGetValueName(off); /* convert base to char* type */ - base = LLVMBuildPointerCast(builder, base, bytep, ""); + base = LLVMBuildPointerCast(builder, base, bytep, name); /* addr = base + off */ - addr = LLVMBuildInBoundsGEP(builder, base, &off, 1, ""); + addr = LLVMBuildInBoundsGEP(builder, base, &off, 1, name); /* convert back to the actual pointer type */ - addr = LLVMBuildPointerCast(builder, addr, type, ""); + addr = LLVMBuildPointerCast(builder, addr, type, name); return addr; } 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, + [OP_FCMP_ORD] = LLVMRealORD, + [OP_FCMP_OEQ] = LLVMRealOEQ, + [OP_FCMP_ONE] = LLVMRealONE, + [OP_FCMP_OLE] = LLVMRealOLE, + [OP_FCMP_OGE] = LLVMRealOGE, + [OP_FCMP_OLT] = LLVMRealOLT, + [OP_FCMP_OGT] = LLVMRealOGT, + [OP_FCMP_UEQ] = LLVMRealUEQ, + [OP_FCMP_UNE] = LLVMRealUNE, + [OP_FCMP_ULE] = LLVMRealULE, + [OP_FCMP_UGE] = LLVMRealUGE, + [OP_FCMP_ULT] = LLVMRealULT, + [OP_FCMP_UGT] = LLVMRealUGT, + [OP_FCMP_UNO] = LLVMRealUNO, }; return trans_tbl[opcode]; @@ -442,109 +536,83 @@ 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); + lhs = get_ioperand(fn, insn->type, insn->src1); + rhs = get_ioperand(fn, insn->type, 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); + 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); + target = LLVMBuildSub(fn->builder, lhs, rhs, target_name); break; - case OP_MULS: - assert(!symbol_is_fp_type(insn->type)); + case OP_MUL: 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); + target = LLVMBuildUDiv(fn->builder, lhs, rhs, target_name); break; case OP_DIVS: - assert(!symbol_is_fp_type(insn->type)); + assert(!is_float_type(insn->type)); target = LLVMBuildSDiv(fn->builder, lhs, rhs, target_name); break; case OP_MODU: - assert(!symbol_is_fp_type(insn->type)); + assert(!is_float_type(insn->type)); target = LLVMBuildURem(fn->builder, lhs, rhs, target_name); break; case OP_MODS: - assert(!symbol_is_fp_type(insn->type)); + assert(!is_float_type(insn->type)); target = LLVMBuildSRem(fn->builder, lhs, rhs, target_name); break; case OP_SHL: - assert(!symbol_is_fp_type(insn->type)); + assert(!is_float_type(insn->type)); target = LLVMBuildShl(fn->builder, lhs, rhs, target_name); break; case OP_LSR: - assert(!symbol_is_fp_type(insn->type)); + assert(!is_float_type(insn->type)); target = LLVMBuildLShr(fn->builder, lhs, rhs, target_name); break; case OP_ASR: - assert(!symbol_is_fp_type(insn->type)); + assert(!is_float_type(insn->type)); target = LLVMBuildAShr(fn->builder, lhs, rhs, target_name); break; + + /* floating-point */ + case OP_FADD: + target = LLVMBuildFAdd(fn->builder, lhs, rhs, target_name); + break; + case OP_FSUB: + target = LLVMBuildFSub(fn->builder, lhs, rhs, target_name); + break; + case OP_FMUL: + target = LLVMBuildFMul(fn->builder, lhs, rhs, target_name); + break; + case OP_FDIV: + target = LLVMBuildFDiv(fn->builder, lhs, rhs, target_name); + break; /* Logical */ case OP_AND: - assert(!symbol_is_fp_type(insn->type)); + assert(!is_float_type(insn->type)); target = LLVMBuildAnd(fn->builder, lhs, rhs, target_name); break; case OP_OR: - assert(!symbol_is_fp_type(insn->type)); + assert(!is_float_type(insn->type)); target = LLVMBuildOr(fn->builder, lhs, rhs, target_name); break; case OP_XOR: - assert(!symbol_is_fp_type(insn->type)); + assert(!is_float_type(insn->type)); target = LLVMBuildXor(fn->builder, lhs, rhs, target_name); break; - case OP_AND_BOOL: { - LLVMValueRef lhs_nz, rhs_nz; - LLVMTypeRef dst_type; - - lhs_nz = LLVMBuildIsNotNull(fn->builder, lhs, ""); - rhs_nz = LLVMBuildIsNotNull(fn->builder, rhs, ""); - target = LLVMBuildAnd(fn->builder, lhs_nz, rhs_nz, target_name); - - dst_type = insn_symbol_type(fn->module, insn); - target = LLVMBuildZExt(fn->builder, target, dst_type, target_name); - break; - } - case OP_OR_BOOL: { - LLVMValueRef lhs_nz, rhs_nz; - LLVMTypeRef dst_type; - - lhs_nz = LLVMBuildIsNotNull(fn->builder, lhs, ""); - rhs_nz = LLVMBuildIsNotNull(fn->builder, rhs, ""); - target = LLVMBuildOr(fn->builder, lhs_nz, rhs_nz, target_name); - - dst_type = insn_symbol_type(fn->module, insn); - target = LLVMBuildZExt(fn->builder, target, dst_type, target_name); - break; - } default: assert(0); break; } + target = adjust_type(fn, insn->type, target); insn->target->priv = target; } @@ -553,25 +621,47 @@ static void output_op_compare(struct function *fn, struct instruction *insn) LLVMValueRef lhs, rhs, target; char target_name[64]; - lhs = pseudo_to_value(fn, insn, insn->src1); - + lhs = pseudo_to_value(fn, NULL, insn->src1); if (insn->src2->type == PSEUDO_VAL) - rhs = LLVMConstInt(LLVMTypeOf(lhs), insn->src2->value, 1); + rhs = constant_value(insn->src2->value, LLVMTypeOf(lhs)); else - rhs = pseudo_to_value(fn, insn, insn->src2); + rhs = pseudo_to_value(fn, NULL, insn->src2); + if (!rhs) + rhs = LLVMGetUndef(symbol_type(insn->type)); pseudo_name(insn->target, target_name); - LLVMTypeRef dst_type = insn_symbol_type(fn->module, insn); + LLVMTypeRef dst_type = insn_symbol_type(insn); - if (LLVMGetTypeKind(LLVMTypeOf(lhs)) == LLVMIntegerTypeKind) { + switch (LLVMGetTypeKind(LLVMTypeOf(lhs))) { + case LLVMPointerTypeKind: + lhs = value_to_pvalue(fn, &ptr_ctype, lhs); + rhs = value_to_pvalue(fn, &ptr_ctype, rhs); + /* fall through */ + + case LLVMIntegerTypeKind: { LLVMIntPredicate op = translate_op(insn->opcode); + if (LLVMGetTypeKind(LLVMTypeOf(rhs)) == LLVMPointerTypeKind) { + LLVMTypeRef ltype = LLVMTypeOf(lhs); + rhs = LLVMBuildPtrToInt(fn->builder, rhs, ltype, ""); + } target = LLVMBuildICmp(fn->builder, op, lhs, rhs, target_name); - } else { + break; + } + case LLVMHalfTypeKind: + case LLVMFloatTypeKind: + case LLVMDoubleTypeKind: + case LLVMX86_FP80TypeKind: + case LLVMFP128TypeKind: + case LLVMPPC_FP128TypeKind: { LLVMRealPredicate op = translate_fop(insn->opcode); target = LLVMBuildFCmp(fn->builder, op, lhs, rhs, target_name); + break; + } + default: + assert(0); } target = LLVMBuildZExt(fn->builder, target, dst_type, target_name); @@ -584,8 +674,7 @@ 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); - + LLVMValueRef result = get_operand(fn, insn->type, pseudo); LLVMBuildRet(fn->builder, result); } else LLVMBuildRetVoid(fn->builder); @@ -602,10 +691,10 @@ static LLVMValueRef calc_memop_addr(struct function *fn, struct instruction *ins off = LLVMConstInt(int_type, insn->offset, 0); /* convert src to the effective pointer type */ - src = pseudo_to_value(fn, insn, insn->src); + src = pseudo_to_value(fn, insn->type, insn->src); as = LLVMGetPointerAddressSpace(LLVMTypeOf(src)); - addr_type = LLVMPointerType(insn_symbol_type(fn->module, insn), as); - src = LLVMBuildPointerCast(fn->builder, src, addr_type, ""); + addr_type = LLVMPointerType(insn_symbol_type(insn), as); + src = LLVMBuildPointerCast(fn->builder, src, addr_type, LLVMGetValueName(src)); /* addr = src + off */ addr = calc_gep(fn->builder, src, off); @@ -616,33 +705,33 @@ static LLVMValueRef calc_memop_addr(struct function *fn, struct instruction *ins static void output_op_load(struct function *fn, struct instruction *insn) { LLVMValueRef addr, target; + char name[MAX_PSEUDO_NAME]; addr = calc_memop_addr(fn, insn); /* perform load */ - target = LLVMBuildLoad(fn->builder, addr, "load_target"); + pseudo_name(insn->target, name); + target = LLVMBuildLoad(fn->builder, addr, name); insn->target->priv = target; } static void output_op_store(struct function *fn, struct instruction *insn) { - LLVMValueRef addr, target, target_in; + LLVMValueRef addr, target_in; addr = calc_memop_addr(fn, insn); - target_in = pseudo_to_value(fn, insn, insn->target); + target_in = pseudo_to_rvalue(fn, insn->type, insn->target); /* perform store */ - target = LLVMBuildStore(fn->builder, target_in, addr); - - insn->target->priv = target; + LLVMBuildStore(fn->builder, target_in, addr); } static LLVMValueRef bool_value(struct function *fn, LLVMValueRef value) { if (LLVMTypeOf(value) != LLVMInt1Type()) - value = LLVMBuildIsNotNull(fn->builder, value, "cond"); + value = LLVMBuildIsNotNull(fn->builder, value, LLVMGetValueName(value)); return value; } @@ -650,7 +739,7 @@ static LLVMValueRef bool_value(struct function *fn, LLVMValueRef value) static void output_op_cbr(struct function *fn, struct instruction *br) { LLVMValueRef cond = bool_value(fn, - pseudo_to_value(fn, br, br->cond)); + pseudo_to_value(fn, NULL, br->cond)); LLVMBuildCondBr(fn->builder, cond, br->bb_true->priv, @@ -665,14 +754,16 @@ static void output_op_br(struct function *fn, struct instruction *br) static void output_op_sel(struct function *fn, struct instruction *insn) { LLVMValueRef target, src1, src2, src3; + char name[MAX_PSEUDO_NAME]; - src1 = bool_value(fn, pseudo_to_value(fn, insn, insn->src1)); - src2 = pseudo_to_value(fn, insn, insn->src2); - src3 = pseudo_to_value(fn, insn, insn->src3); + src1 = bool_value(fn, pseudo_to_value(fn, NULL, insn->src1)); + src2 = get_operand(fn, insn->type, insn->src2); + src3 = get_operand(fn, insn->type, insn->src3); - target = LLVMBuildSelect(fn->builder, src1, src2, src3, "select"); + pseudo_name(insn->target, name); + target = LLVMBuildSelect(fn->builder, src1, src2, src3, name); - insn->target->priv = target; + insn->target->priv = adjust_type(fn, insn->type, target); } static void output_op_switch(struct function *fn, struct instruction *insn) @@ -683,51 +774,52 @@ static void output_op_switch(struct function *fn, struct instruction *insn) 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); + if (jmp->begin <= jmp->end) { + n_jmp += (jmp->end - jmp->begin) + 1; } else /* default case */ def = jmp->target; } END_FOR_EACH_PTR(jmp); - sw_val = pseudo_to_value(fn, insn, insn->target); + sw_val = get_ioperand(fn, insn->type, insn->cond); 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); + long long val; + + for (val = jmp->begin; val <= jmp->end; val++) { + LLVMValueRef Val = val_to_value(val, insn->type); + LLVMAddCase(target, Val, jmp->target->priv); } } END_FOR_EACH_PTR(jmp); - - insn->target->priv = target; } static void output_op_call(struct function *fn, struct instruction *insn) { LLVMValueRef target, func; + struct symbol *ctype; int n_arg = 0, i; struct pseudo *arg; LLVMValueRef *args; + char name[64]; - FOR_EACH_PTR(insn->arguments, arg) { - n_arg++; - } END_FOR_EACH_PTR(arg); - + n_arg = pseudo_list_size(insn->arguments); args = calloc(n_arg, sizeof(LLVMValueRef)); + PREPARE_PTR_LIST(insn->fntypes, ctype); + if (insn->func->type == PSEUDO_REG || insn->func->type == PSEUDO_PHI) + func = get_operand(fn, ctype, insn->func); + else + func = pseudo_to_value(fn, ctype, insn->func); i = 0; FOR_EACH_PTR(insn->arguments, arg) { - args[i++] = pseudo_to_value(fn, insn, arg); + NEXT_PTR_LIST(ctype); + args[i++] = pseudo_to_rvalue(fn, ctype, arg); } END_FOR_EACH_PTR(arg); + FINISH_PTR_LIST(ctype); - func = pseudo_to_value(fn, insn, insn->func); - target = LLVMBuildCall(fn->builder, func, args, n_arg, ""); + pseudo_name(insn->target, name); + target = LLVMBuildCall(fn->builder, func, args, n_arg, name); insn->target->priv = target; } @@ -740,7 +832,7 @@ static void output_op_phisrc(struct function *fn, struct instruction *insn) assert(insn->target->priv == NULL); /* target = src */ - v = pseudo_to_value(fn, insn, insn->phi_src); + v = get_operand(fn, insn->type, insn->phi_src); FOR_EACH_PTR(insn->phi_users, phi) { LLVMValueRef load, ptr; @@ -770,42 +862,127 @@ static void output_op_phi(struct function *fn, struct instruction *insn) static void output_op_ptrcast(struct function *fn, struct instruction *insn) { LLVMValueRef src, target; + LLVMTypeRef dtype; + struct symbol *otype = insn->orig_type; + LLVMOpcode op; char target_name[64]; - src = insn->src->priv; - if (!src) - src = pseudo_to_value(fn, insn, insn->src); - + src = get_operand(fn, otype, 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); + dtype = symbol_type(insn->type); + switch (insn->opcode) { + case OP_UTPTR: + case OP_SEXT: // FIXME + assert(is_int_type(otype)); + assert(is_ptr_type(insn->type)); + op = LLVMIntToPtr; + break; + case OP_PTRTU: + assert(is_ptr_type(otype)); + assert(is_int_type(insn->type)); + op = LLVMPtrToInt; + break; + case OP_PTRCAST: + case OP_ZEXT: // FIXME + assert(is_ptr_type(otype)); + assert(is_ptr_type(insn->type)); + op = LLVMBitCast; + break; + default: + assert(0); + } + target = LLVMBuildCast(fn->builder, op, src, dtype, target_name); insn->target->priv = target; } static void output_op_cast(struct function *fn, struct instruction *insn, LLVMOpcode op) { LLVMValueRef src, target; + LLVMTypeRef dtype; + struct symbol *otype = insn->orig_type; char target_name[64]; - src = insn->src->priv; - if (!src) - src = pseudo_to_value(fn, insn, insn->src); + if (is_ptr_type(insn->type)) // cast to void* is OP_CAST ... + return output_op_ptrcast(fn, insn); + assert(is_int_type(insn->type)); + + src = get_operand(fn, otype, insn->src); pseudo_name(insn->target, target_name); - assert(!symbol_is_fp_type(insn->type)); + dtype = symbol_type(insn->type); + if (is_ptr_type(otype)) { + op = LLVMPtrToInt; + } else if (is_float_type(otype)) { + assert(op == LLVMFPToUI || op == LLVMFPToSI); + } else if (is_int_type(otype)) { + unsigned int width = otype->bit_size; + if (insn->size < width) + op = LLVMTrunc; + else if (insn->size == width) + op = LLVMBitCast; + } else { + assert(0); + } - 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); + target = LLVMBuildCast(fn->builder, op, src, dtype, target_name); + insn->target->priv = target; +} + +static void output_op_fpcast(struct function *fn, struct instruction *insn) +{ + LLVMTypeRef dtype = symbol_type(insn->type); + LLVMValueRef src, target; + struct symbol *otype = insn->orig_type; + char name[64]; + + assert(is_float_type(insn->type)); + + pseudo_name(insn->target, name); + src = get_operand(fn, otype, insn->src); + switch (insn->opcode) { + case OP_FCVTF: + target = LLVMBuildFPCast(fn->builder, src, dtype, name); + break; + case OP_SCVTF: + target = LLVMBuildSIToFP(fn->builder, src, dtype, name); + break; + case OP_UCVTF: + target = LLVMBuildUIToFP(fn->builder, src, dtype, name); + break; + default: + assert(0); + } + insn->target->priv = target; +} + +static void output_op_setval(struct function *fn, struct instruction *insn) +{ + struct expression *val = insn->val; + LLVMValueRef target; + + switch (val->type) { + case EXPR_LABEL: + target = LLVMBlockAddress(fn->fn, val->symbol->bb_target->priv); + break; + default: + assert(0); + } insn->target->priv = target; } +static void output_op_setfval(struct function *fn, struct instruction *insn) +{ + LLVMTypeRef dtype = symbol_type(insn->type); + LLVMValueRef target; + + target = LLVMConstReal(dtype, insn->fvalue); + insn->target->priv = target; +} + static void output_insn(struct function *fn, struct instruction *insn) { switch (insn->opcode) { @@ -822,7 +999,10 @@ static void output_insn(struct function *fn, struct instruction *insn) assert(0); break; case OP_SETVAL: - assert(0); + output_op_setval(fn, insn); + break; + case OP_SETFVAL: + output_op_setfval(fn, insn); break; case OP_SWITCH: output_op_switch(fn, insn); @@ -839,37 +1019,42 @@ static void output_insn(struct function *fn, struct instruction *insn) 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: + case OP_ZEXT: output_op_cast(fn, insn, LLVMZExt); break; - case OP_SCAST: + case OP_SEXT: output_op_cast(fn, insn, LLVMSExt); break; - case OP_FPCAST: - assert(0); + case OP_TRUNC: + output_op_cast(fn, insn, LLVMTrunc); break; + case OP_FCVTU: + output_op_cast(fn, insn, LLVMFPToUI); + break; + case OP_FCVTS: + output_op_cast(fn, insn, LLVMFPToSI); + break; + case OP_UCVTF: case OP_SCVTF: + case OP_FCVTF: + output_op_fpcast(fn, insn); + break; + case OP_UTPTR: + case OP_PTRTU: case OP_PTRCAST: output_op_ptrcast(fn, insn); break; case OP_BINARY ... OP_BINARY_END: output_op_binary(fn, insn); break; - case OP_BINCMP ... OP_BINCMP_END: + case OP_FPCMP ... OP_BINCMP_END: output_op_compare(fn, insn); break; case OP_SEL: @@ -882,7 +1067,7 @@ static void output_insn(struct function *fn, struct instruction *insn) LLVMValueRef src, target; char target_name[64]; - src = pseudo_to_value(fn, insn, insn->src); + src = pseudo_to_value(fn, insn->type, insn->src); pseudo_name(insn->target, target_name); @@ -891,9 +1076,23 @@ static void output_insn(struct function *fn, struct instruction *insn) insn->target->priv = target; break; } - case OP_NEG: - assert(0); + case OP_FNEG: + case OP_NEG: { + LLVMValueRef src, target; + char target_name[64]; + + src = pseudo_to_value(fn, insn->type, insn->src); + + pseudo_name(insn->target, target_name); + + if (insn->opcode == OP_FNEG) + target = LLVMBuildFNeg(fn->builder, src, target_name); + else + target = LLVMBuildNeg(fn->builder, src, target_name); + + insn->target->priv = target; break; + } case OP_CONTEXT: assert(0); break; @@ -916,12 +1115,10 @@ static void output_insn(struct function *fn, struct instruction *insn) } } -static void output_bb(struct function *fn, struct basic_block *bb, unsigned long generation) +static void output_bb(struct function *fn, struct basic_block *bb) { struct instruction *insn; - bb->generation = generation; - FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb) continue; @@ -935,43 +1132,33 @@ static void output_bb(struct function *fn, struct basic_block *bb, unsigned long 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; + int i; - 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); + function.fn = get_sym_value(module, sym); LLVMSetFunctionCallConv(function.fn, LLVMCCallConv); - LLVMSetLinkage(function.fn, function_linkage(sym)); function.builder = LLVMCreateBuilder(); - static int nr_bb; + /* give a name to each argument */ + nr_args = symbol_list_size(base_type->arguments); + for (i = 0; i < nr_args; i++) { + char name[MAX_PSEUDO_NAME]; + LLVMValueRef arg; - FOR_EACH_PTR(ep->bbs, bb) { - if (bb->generation == generation) - continue; + arg = LLVMGetParam(function.fn, i); + snprintf(name, sizeof(name), "ARG%d.", i+1); + LLVMSetValueName(arg, name); + } + /* create the BBs */ + FOR_EACH_PTR(ep->bbs, bb) { + static int nr_bb; LLVMBasicBlockRef bbr; char bbname[32]; struct instruction *insn; @@ -992,7 +1179,7 @@ static void output_fn(LLVMModuleRef module, struct entrypoint *ep) /* insert alloca into entry block */ entrybbr = LLVMGetEntryBasicBlock(function.fn); LLVMPositionBuilderAtEnd(function.builder, entrybbr); - phi_type = insn_symbol_type(module, insn); + phi_type = insn_symbol_type(insn); ptr = LLVMBuildAlloca(function.builder, phi_type, ""); /* emit forward load for phi */ LLVMClearInsertionPosition(function.builder); @@ -1002,12 +1189,9 @@ static void output_fn(LLVMModuleRef module, struct entrypoint *ep) 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); + output_bb(&function, bb); } END_FOR_EACH_PTR(bb); } @@ -1022,7 +1206,10 @@ static LLVMValueRef output_data(LLVMModuleRef module, struct symbol *sym) if (initializer) { switch (initializer->type) { case EXPR_VALUE: - initial_value = LLVMConstInt(symbol_type(module, sym), initializer->value, 1); + initial_value = LLVMConstInt(symbol_type(sym), initializer->value, 1); + break; + case EXPR_FVALUE: + initial_value = LLVMConstReal(symbol_type(sym), initializer->fvalue); break; case EXPR_SYMBOL: { struct symbol *sym = initializer->symbol; @@ -1039,15 +1226,20 @@ static LLVMValueRef output_data(LLVMModuleRef module, struct symbol *sym) break; } default: - assert(0); + warning(initializer->pos, "can't initialize type: %s", show_typename(sym)); + initial_value = NULL; + break; } } else { - LLVMTypeRef type = symbol_type(module, sym); + LLVMTypeRef type = symbol_type(sym); initial_value = LLVMConstNull(type); } - name = show_ident(sym->ident); + if (!initial_value) + return NULL; + + name = sym->ident ? show_ident(sym->ident) : "" ; data = LLVMAddGlobal(module, LLVMTypeOf(initial_value), name); @@ -1080,8 +1272,11 @@ static int compile(LLVMModuleRef module, struct symbol_list *list) struct entrypoint *ep; expand_symbol(sym); - if (is_prototype(sym)) + if (is_prototype(sym)) { + // this will do the LLVMAddFunction() we want + get_sym_value(module, sym); continue; + } ep = linearize_symbol(sym); if (ep) @@ -1158,12 +1353,12 @@ int main(int argc, char **argv) /* need ->phi_users */ dbg_dead = 1; - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { symlist = sparse(file); if (die_if_error) return 1; compile(module, symlist); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); LLVMVerifyModule(module, LLVMPrintMessageAction, NULL); diff --git a/usr/src/tools/smatch/src/sparse.1 b/usr/src/tools/smatch/src/sparse.1 index 9be66c4c05..f4585993f4 100644 --- a/usr/src/tools/smatch/src/sparse.1 +++ b/usr/src/tools/smatch/src/sparse.1 @@ -20,6 +20,12 @@ off those warnings, pass the negation of the associated warning option, . .SH WARNING OPTIONS .TP +.B \-fmax-warnings=COUNT +Set the maximum number of displayed warnings to COUNT, which should be +a numerical value or 'unlimited'. +The default limit is 100. +. +.TP .B \-Wsparse\-all Turn on all sparse warnings, except for those explicitly disabled via \fB\-Wno\-something\fR. @@ -31,12 +37,20 @@ Turn all sparse warnings into errors. Warn about code which mixes pointers to different address spaces. Sparse allows an extended attribute -.BI __attribute__((address_space( num ))) -on pointers, which designates a pointer target in address space \fInum\fR (a -constant integer). With \fB\-Waddress\-space\fR, Sparse treats pointers with -identical target types but different address spaces as distinct types. To -override this warning, such as for functions which convert pointers between -address spaces, use a type that includes \fB__attribute__((force))\fR. +.BI __attribute__((address_space( id ))) +on pointers, which designates a pointer target in address space \fIid\fR (an +identifier or a constant integer). +With \fB\-Waddress\-space\fR, Sparse treats pointers with +identical target types but different address spaces as distinct types and +will warn accordingly. + +Sparse will also warn on casts which remove the address space (casts to an +integer type or to a plain pointer type). An exception to this is when the +destination type is \fBuintptr_t\fR (or \fBunsigned long\fR) since such casts +are often used to "get a pointer value representation in an integer type" and +such values are independent of the address space. + +To override these warnings, use a type that includes \fB__attribute__((force))\fR. Sparse issues these warnings by default. To turn them off, use \fB\-Wno\-address\-space\fR. @@ -71,10 +85,27 @@ Sparse issues these warnings by default. To turn them off, use \fB\-Wno\-bitwise\fR. . .TP +.B \-Wbitwise\-pointer +Same as \fB\-Wbitwise\fR but for casts to or from pointers to bitwise types. + +Sparse does not issue these warnings by default. +. +.TP +.B \-Wcast\-from\-as +Warn about casts which remove an address space from a pointer type. + +This is similar to \fB\-Waddress\-space\fR but will also warn +on casts to \fBunsigned long\fR. + +Sparse does not issues these warnings by default. +. +.TP .B \-Wcast\-to\-as Warn about casts which add an address space to a pointer type. A cast that includes \fB__attribute__((force))\fR will suppress this warning. +No warning is generated if the original type is \fBuintptr_t\fR +(or \fBunsigned long\fR). Sparse does not issue these warnings by default. . @@ -213,13 +244,6 @@ Sparse issues these warnings by default. To turn them off, use \fB\-Wno\-enum\-mismatch\fR. . .TP -.B \-Wempty\-character\-constant -Warn about a constant such as ''. - -Sparse issues these warnings by default. To turn them off, use -\fB\-Wno\-empty\-character\-constant\fR. -. -.TP .B \-Wexternal\-function\-has\-definition Warn about function definitions that are declared with external linkage. @@ -256,13 +280,6 @@ The limit can be changed with \fB\-fmemcpy\-max\-count=COUNT\fR, the default being \fB100000\fR. . .TP -.B \-Wnon\-ansi\-function\-declaration -Warn about non-ANSI function declarations. - -Sparse issues these warnings by default. To turn them off, use -\fB\-Wno\-non\-ansi\-function\-declaration\fR. -. -.TP .B \-Wnon\-pointer\-null Warn about the use of 0 as a NULL pointer. @@ -366,10 +383,22 @@ Such declarations can lead to error-prone code. Sparse does not issue these warnings by default. . .TP +.B \-Wshift-count-negative +Warn if a shift count is negative. + +Sparse issues these warnings by default. +. +.TP +.B \-Wshift-count-overflow +Warn if a shift count is bigger than the operand's width. + +Sparse issues these warnings by default. +. +.TP .B \-Wsizeof-bool Warn when checking the sizeof a _Bool. -C99 does not specify the sizeof a _Bool. gcc uses 1. +C99 does not specify the size of a _Bool. GCC, by default, uses \fI1\fR. Sparse does not issue these warnings by default. . @@ -412,19 +441,20 @@ normalized GNU triplet. (e.g. i386-linux-gnu). . .SH DEBUG OPTIONS .TP -.B \-fdump-linearize[=only] -Dump the IR code of a function directly after its linearization, -before any simplifications are made. If the argument \fB=only\fR is -also given no further processing is done on the function. -. .B \-fmem-report Report some statistics about memory allocation used by the tool. . .SH OTHER OPTIONS .TP +.B \-fdiagnostic-prefix[=PREFIX] +Prefix all diagnostics by the given PREFIX, followed by ": ". +If no one is given "sparse" is used. +The default is to not use a prefix at all. +. +.TP .B \-fmemcpy-max-count=COUNT Set the limit for the warnings given by \fB-Wmemcpy-max-count\fR. -A COUNT of 0, useless in itself, will effectively disable the warning. +A COUNT of 'unlimited' or '0' will effectively disable the warning. The default limit is 100000. . .TP @@ -433,6 +463,11 @@ Set the distance between tab stops. This helps sparse report correct column numbers in warnings or errors. If the value is less than 1 or greater than 100, the option is ignored. The default is 8. . +.TP +.B \-f[no-]unsigned-char, \-f[no-]signed-char +Let plain 'char' be unsigned or signed. +By default chars are signed. +. .SH SEE ALSO .BR cgcc (1) . @@ -442,5 +477,18 @@ http://www.kernel.org/pub/software/devel/sparse/ .SH MAILING LIST linux-sparse@vger.kernel.org . -.SH MAINTAINER -Christopher Li <sparse@chrisli.org> +.SH CONTRIBUTINGS AND REPORTING BUGS +Submission of patches and reporting of bugs, as well as discussions +related to Sparse, should be done via the mailing list (linux-sparse@vger.kernel.org) +where the development and maintenance is primarily done. +You do not have to be subscribed to the list to send a message there. + +Bugs can also be reported and tracked via the Linux kernel's bugzilla: +http://bugzilla.kernel.org/enter_bug.cgi?component=Sparse&product=Tools . +. +.SH AUTHORS +Sparse was started by Linus Torvalds. +The complete list of contributors can be find at +https://www.openhub.net/p/sparse/contributors . + +Luc Van Oostenryck is Sparse's current maintainer. diff --git a/usr/src/tools/smatch/src/sparse.c b/usr/src/tools/smatch/src/sparse.c index bceacd94e6..151eaf4ef5 100644 --- a/usr/src/tools/smatch/src/sparse.c +++ b/usr/src/tools/smatch/src/sparse.c @@ -47,6 +47,8 @@ static int context_increase(struct basic_block *bb, int entry) FOR_EACH_PTR(bb->insns, insn) { int val; + if (!insn->bb) + continue; if (insn->opcode != OP_CONTEXT) continue; val = insn->increment; @@ -120,7 +122,7 @@ static void check_cast_instruction(struct instruction *insn) int old = orig_type->bit_size; int new = insn->size; int oldsigned = (orig_type->ctype.modifiers & MOD_SIGNED) != 0; - int newsigned = insn->opcode == OP_SCAST; + int newsigned = insn->opcode == OP_SEXT; if (new > old) { if (oldsigned == newsigned) @@ -214,7 +216,8 @@ static void check_call_instruction(struct instruction *insn) static void check_one_instruction(struct instruction *insn) { switch (insn->opcode) { - case OP_CAST: case OP_SCAST: + case OP_SEXT: case OP_ZEXT: + case OP_TRUNC: if (verbose) check_cast_instruction(insn); break; @@ -243,6 +246,7 @@ static void check_instructions(struct entrypoint *ep) { struct basic_block *bb; FOR_EACH_PTR(ep->bbs, bb) { + bb->context = -1; check_bb_instructions(bb); } END_FOR_EACH_PTR(bb); } @@ -271,6 +275,37 @@ static void check_context(struct entrypoint *ep) check_bb_context(ep, ep->entry->bb, in_context, out_context); } +/* list_compound_symbol - symbol info for arrays, structures, unions */ +static void list_compound_symbol(struct symbol *sym) +{ + struct symbol *base; + + /* Only show symbols that have a positive size */ + if (sym->bit_size <= 0) + return; + if (!sym->ctype.base_type) + return; + /* Don't show unnamed types */ + if (!sym->ident) + return; + + if (sym->type == SYM_NODE) + base = sym->ctype.base_type; + else + base = sym; + switch (base->type) { + case SYM_STRUCT: case SYM_UNION: case SYM_ARRAY: + break; + default: + return; + } + + info(sym->pos, "%s: compound size %u, alignment %lu", + show_typename(sym), + bits_to_bytes(sym->bit_size), + sym->ctype.alignment); +} + static void check_symbols(struct symbol_list *list) { struct symbol *sym; @@ -280,12 +315,14 @@ static void check_symbols(struct symbol_list *list) expand_symbol(sym); ep = linearize_symbol(sym); - if (ep) { + if (ep && ep->entry) { if (dbg_entry) show_entry(ep); check_context(ep); } + if (dbg_compound) + list_compound_symbol(sym); } END_FOR_EACH_PTR(sym); if (Wsparse_error && die_if_error) @@ -297,11 +334,14 @@ int main(int argc, char **argv) struct string_list *filelist = NULL; char *file; + // by default ignore -o <file> + do_output = 0; + // Expand, linearize and show it. check_symbols(sparse_initialize(argc, argv, &filelist)); - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { check_symbols(sparse(file)); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); report_stats(); return 0; diff --git a/usr/src/tools/smatch/src/sparse.pc.in b/usr/src/tools/smatch/src/sparse.pc.in deleted file mode 100644 index f1281c97a5..0000000000 --- a/usr/src/tools/smatch/src/sparse.pc.in +++ /dev/null @@ -1,9 +0,0 @@ -prefix=@prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: Sparse -Description: Semantic parser for C -Version: @version@ -Libs: -L${libdir} -lsparse -Cflags: -I${includedir} diff --git a/usr/src/tools/smatch/src/sparsec b/usr/src/tools/smatch/src/sparsec index 9dc96c956e..bafe2da5d5 100755 --- a/usr/src/tools/smatch/src/sparsec +++ b/usr/src/tools/smatch/src/sparsec @@ -29,20 +29,29 @@ while [ $# -gt 0 ]; do shift done -TMPLLVM=`mktemp -t tmp.XXXXXX`".llvm" -TMPFILE=`mktemp -t tmp.XXXXXX`".o" +TMPFILE=`mktemp -t tmp.XXXXXX` -$DIRNAME/sparse-llvm $SPARSEOPTS > $TMPLLVM LLC=`"${LLVM_CONFIG:-llvm-config}" --bindir`/llc -$LLC -o - $TMPLLVM | as -o $TMPFILE +LLC_ARCH_OPTS= +case "$(uname -s)" in +*CYGWIN*) + # cygwin uses the sjlj (setjmp-longjmp) exception model + LLC_ARCH_OPTS="-exception-model=sjlj" + ;; +*) + ;; +esac + +$DIRNAME/sparse-llvm $SPARSEOPTS | $LLC ${LLC_ARCH_OPTS} | as -o $TMPFILE if [ $NEED_LINK -eq 1 ]; then if [ -z $OUTFILE ]; then OUTFILE=a.out fi gcc $TMPFILE -o $OUTFILE + rm -f $TMPFILE else if [ -z $OUTFILE ]; then echo "`basename $0`: no output file" @@ -50,5 +59,3 @@ else fi mv $TMPFILE $OUTFILE fi - -rm -f $TMPLLVM diff --git a/usr/src/tools/smatch/src/sparsei b/usr/src/tools/smatch/src/sparsei index 3431a9f0b5..3232200e92 100755 --- a/usr/src/tools/smatch/src/sparsei +++ b/usr/src/tools/smatch/src/sparsei @@ -2,6 +2,9 @@ set +e +SPARSEOPTS= +JIT_OPT= + DIRNAME=`dirname $0` LLI=`"${LLVM_CONFIG:-llvm-config}" --bindir`/lli @@ -10,4 +13,19 @@ if [ $# -eq 0 ]; then exit 1 fi -$DIRNAME/sparse-llvm $@ | $LLI +while [ $# -gt 0 ]; do + case $1 in + --jit) + JIT_OPT= + ;; + --no-jit) + JIT_OPT="-force-interpreter" + ;; + *) + SPARSEOPTS="$SPARSEOPTS $1 " + ;; + esac + shift +done + +$DIRNAME/sparse-llvm ${SPARSEOPTS} | $LLI ${JIT_OPT} diff --git a/usr/src/tools/smatch/src/ssa.c b/usr/src/tools/smatch/src/ssa.c new file mode 100644 index 0000000000..3e8800507f --- /dev/null +++ b/usr/src/tools/smatch/src/ssa.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: MIT +// +// SSA conversion +// Copyright (C) 2005 Luc Van Oostenryck +// + +#include <assert.h> +#include "ssa.h" +#include "lib.h" +#include "sset.h" +#include "dominate.h" +#include "flowgraph.h" +#include "linearize.h" +#include "flow.h" // for convert_load_instruction() + + +// Is it possible and desirable for this to be promoted to a pseudo? +static inline bool is_promotable(struct symbol *type) +{ + struct symbol *member; + int bf_seen; + int nbr; + + if (type->type == SYM_NODE) + type = type->ctype.base_type; + switch (type->type) { + case SYM_ENUM: + case SYM_BITFIELD: + case SYM_PTR: + case SYM_RESTRICT: // OK, always integer types + return 1; + case SYM_STRUCT: + // we allow a single scalar field + // but a run of bitfields count for 1 + nbr = 0; + bf_seen = 0; + FOR_EACH_PTR(type->symbol_list, member) { + if (is_bitfield_type(member)) { + if (bf_seen) + continue; + bf_seen = 1; + } else { + bf_seen = 0; + } + if (!is_scalar_type(member)) + return 0; + if (nbr++) + return 0; + } END_FOR_EACH_PTR(member); + if (bf_seen && (type->bit_size > long_ctype.bit_size)) + return 0; + return 1; + case SYM_UNION: + // FIXME: should be like struct but has problem + // when used with/for type cohercion + // -----> OK if only same sized integral types + FOR_EACH_PTR(type->symbol_list, member) { + if (member->bit_size != type->bit_size) + return 0; + if (!is_integral_type(member)) + return 0; + } END_FOR_EACH_PTR(member); + return 1; + default: + break; + } + if (type->ctype.base_type == &int_type) + return 1; + if (type->ctype.base_type == &fp_type) + return 1; + return 0; +} + +static bool insn_before(struct instruction *a, struct instruction *b) +{ + struct basic_block *bb = a->bb; + struct instruction *insn; + + assert(b->bb == bb); + FOR_EACH_PTR(bb->insns, insn) { + if (insn == a) + return true; + if (insn == b) + return false; + } END_FOR_EACH_PTR(insn); + assert(0); +} + +static void kill_store(struct instruction *insn) +{ + remove_use(&insn->src); + remove_use(&insn->target); + insn->bb = NULL; +} + +static void rewrite_local_var(struct basic_block *bb, pseudo_t addr, int nbr_stores, int nbr_uses) +{ + struct instruction *insn; + pseudo_t val = NULL; + + if (!bb) + return; + + FOR_EACH_PTR(bb->insns, insn) { + + if (!insn->bb || insn->src != addr) + continue; + switch (insn->opcode) { + case OP_LOAD: + if (!val) + val = undef_pseudo(); + convert_load_instruction(insn, val); + break; + case OP_STORE: + val = insn->target; + // can't use kill_instruction() unless + // we add a fake user to val + kill_store(insn); + break; + } + } END_FOR_EACH_PTR(insn); +} + +static bool rewrite_single_store(struct instruction *store) +{ + pseudo_t addr = store->src; + struct pseudo_user *pu; + + FOR_EACH_PTR(addr->users, pu) { + struct instruction *insn = pu->insn; + + if (insn->opcode != OP_LOAD) + continue; + + // Let's try to replace the value of the load + // by the value from the store. This is only valid + // if the store dominate the load. + + if (insn->bb == store->bb) { + // the load and the store are in the same BB + // we can convert if the load is after the store. + if (!insn_before(store, insn)) + continue; + } else if (!domtree_dominates(store->bb, insn->bb)) { + // we can't convert this load + continue; + } + + // OK, we can rewrite this load + + // undefs ? + + convert_load_instruction(insn, store->target); + } END_FOR_EACH_PTR(pu); + + // is there some unconverted loads? + if (pseudo_user_list_size(addr->users) > 1) + return false; + + kill_store(store); + return true; +} + +static struct sset *processed; + +// we would like to know: +// is there one or more stores? +// are all loads & stores local/done in a single block? +static void ssa_convert_one_var(struct entrypoint *ep, struct symbol *var) +{ + struct basic_block_list *alpha = NULL; + struct basic_block_list *idf = NULL; + struct basic_block *samebb = NULL; + struct instruction *store = NULL; + struct basic_block *bb; + struct pseudo_user *pu; + unsigned long mod = var->ctype.modifiers; + bool local = true; + int nbr_stores = 0; + int nbr_uses = 0; + pseudo_t addr; + + /* Never used as a symbol? */ + addr = var->pseudo; + if (!addr) + return; + + /* We don't do coverage analysis of volatiles.. */ + if (mod & MOD_VOLATILE) + return; + + /* ..and symbols with external visibility need more care */ + mod &= (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE); + if (mod) + goto external_visibility; + + if (!is_promotable(var)) + return; + + // 1) insert in the worklist all BBs that may modify var + sset_reset(processed); + FOR_EACH_PTR(addr->users, pu) { + struct instruction *insn = pu->insn; + struct basic_block *bb = insn->bb; + + switch (insn->opcode) { + case OP_STORE: + nbr_stores++; + store = insn; + if (!sset_testset(processed, bb->nr)) + add_bb(&alpha, bb); + /* fall through */ + case OP_LOAD: + if (local) { + if (!samebb) + samebb = bb; + else if (samebb != bb) + local = false; + } + nbr_uses++; + break; + case OP_SYMADDR: + mod |= MOD_ADDRESSABLE; + goto external_visibility; + default: + warning(var->pos, "symbol '%s' pseudo used in unexpected way", + show_ident(var->ident)); + } + } END_FOR_EACH_PTR(pu); + + if (nbr_stores == 1) { + if (rewrite_single_store(store)) + return; + } + + // if all uses are local to a single block + // they can easily be rewritten and doesn't need phi-nodes + // FIXME: could be done for extended BB too + if (local) { + rewrite_local_var(samebb, addr, nbr_stores, nbr_uses); + return; + } + + idf_compute(ep, &idf, alpha); + FOR_EACH_PTR(idf, bb) { + struct instruction *node = insert_phi_node(bb, var); + node->phi_var = var->pseudo; + } END_FOR_EACH_PTR(bb); + var->torename = 1; + +external_visibility: + if (mod & (MOD_NONLOCAL | MOD_STATIC)) + return; + kill_dead_stores(ep, addr, !mod); +} + +static pseudo_t lookup_var(struct basic_block *bb, struct symbol *var) +{ + do { + pseudo_t val = phi_map_lookup(bb->phi_map, var); + if (val) + return val; + } while ((bb = bb->idom)); + return undef_pseudo(); +} + +static struct instruction_list *phis_all; +static struct instruction_list *phis_used; + +static void ssa_rename_insn(struct basic_block *bb, struct instruction *insn) +{ + struct symbol *var; + pseudo_t addr; + pseudo_t val; + + switch (insn->opcode) { + case OP_STORE: + addr = insn->src; + if (addr->type != PSEUDO_SYM) + break; + var = addr->sym; + if (!var || !var->torename) + break; + phi_map_update(&bb->phi_map, var, insn->target); + kill_store(insn); + break; + case OP_LOAD: + addr = insn->src; + if (addr->type != PSEUDO_SYM) + break; + var = addr->sym; + if (!var || !var->torename) + break; + val = lookup_var(bb, var); + convert_load_instruction(insn, val); + break; + case OP_PHI: + var = insn->type; + if (!var || !var->torename) + break; + phi_map_update(&bb->phi_map, var, insn->target); + add_instruction(&phis_all, insn); + break; + } +} + +static void ssa_rename_insns(struct entrypoint *ep) +{ + struct basic_block *bb; + + FOR_EACH_PTR(ep->bbs, bb) { + struct instruction *insn; + FOR_EACH_PTR(bb->insns, insn) { + if (!insn->bb) + continue; + ssa_rename_insn(bb, insn); + } END_FOR_EACH_PTR(insn); + } END_FOR_EACH_PTR(bb); +} + +static void mark_phi_used(pseudo_t val) +{ + struct instruction *node; + + if (val->type != PSEUDO_REG) + return; + node = val->def; + if (node->opcode != OP_PHI) + return; + if (node->used) + return; + node->used = 1; + add_instruction(&phis_used, node); +} + +static void ssa_rename_phi(struct instruction *insn) +{ + struct basic_block *par; + struct symbol *var; + + if (!insn->phi_var) + return; + var = insn->phi_var->sym; + if (!var->torename) + return; + FOR_EACH_PTR(insn->bb->parents, par) { + struct instruction *term = delete_last_instruction(&par->insns); + pseudo_t val = lookup_var(par, var); + pseudo_t phi = alloc_phi(par, val, var); + phi->ident = var->ident; + add_instruction(&par->insns, term); + use_pseudo(insn, phi, add_pseudo(&insn->phi_list, phi)); + mark_phi_used(val); + } END_FOR_EACH_PTR(par); +} + +static void ssa_rename_phis(struct entrypoint *ep) +{ + struct instruction *phi; + + phis_used = NULL; + FOR_EACH_PTR(phis_all, phi) { + if (has_users(phi->target)) { + phi->used = 1; + add_instruction(&phis_used, phi); + } + } END_FOR_EACH_PTR(phi); + + FOR_EACH_PTR(phis_used, phi) { + if (!phi->bb) + continue; + ssa_rename_phi(phi); + } END_FOR_EACH_PTR(phi); +} + +void ssa_convert(struct entrypoint *ep) +{ + struct basic_block *bb; + pseudo_t pseudo; + int first, last; + + // calculate the number of BBs + first = ep->entry->bb->nr; + last = first; + FOR_EACH_PTR(ep->bbs, bb) { + int nr = bb->nr; + if (nr > last) + last = nr; + } END_FOR_EACH_PTR(bb); + + processed = sset_init(first, last); + + // try to promote memory accesses to pseudos + FOR_EACH_PTR(ep->accesses, pseudo) { + ssa_convert_one_var(ep, pseudo->sym); + } END_FOR_EACH_PTR(pseudo); + + // rename the converted accesses + phis_all = phis_used = NULL; + ssa_rename_insns(ep); + ssa_rename_phis(ep); +} diff --git a/usr/src/tools/smatch/src/ssa.h b/usr/src/tools/smatch/src/ssa.h new file mode 100644 index 0000000000..8537ac7b27 --- /dev/null +++ b/usr/src/tools/smatch/src/ssa.h @@ -0,0 +1,8 @@ +#ifndef SSA_H +#define SSA_H + +struct entrypoint; + +void ssa_convert(struct entrypoint *ep); + +#endif diff --git a/usr/src/tools/smatch/src/sset.c b/usr/src/tools/smatch/src/sset.c new file mode 100644 index 0000000000..e9681e00dd --- /dev/null +++ b/usr/src/tools/smatch/src/sset.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +// +// sset.c - an all O(1) implementation of sparse sets as presented in: +// "An Efficient Representation for Sparse Sets" +// by Preston Briggs and Linda Torczon +// +// Copyright (C) 2017 - Luc Van Oostenryck + +#include "sset.h" +#include "lib.h" +#include <stdlib.h> + + +struct sset *sset_init(unsigned int first, unsigned int last) +{ + unsigned int size = last - first + 1; + struct sset *s = malloc(sizeof(*s) + size * 2 * sizeof(s->sets[0])); + + s->size = size; + s->off = first; + s->nbr = 0; + return s; +} + +void sset_free(struct sset *s) +{ + free(s); +} diff --git a/usr/src/tools/smatch/src/sset.h b/usr/src/tools/smatch/src/sset.h new file mode 100644 index 0000000000..69cee18a27 --- /dev/null +++ b/usr/src/tools/smatch/src/sset.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT + +#ifndef SSET_H +#define SSET_H + +/* + * sset.h - an all O(1) implementation of sparse sets as presented in: + * "An Efficient Representation for Sparse Sets" + * by Preston Briggs and Linda Torczon + * + * Copyright (C) 2017 - Luc Van Oostenryck + */ + +#include <stdbool.h> + +struct sset { + unsigned int nbr; + unsigned int off; + unsigned int size; + unsigned int sets[0]; +}; + +extern struct sset *sset_init(unsigned int size, unsigned int off); +extern void sset_free(struct sset *s); + + +static inline void sset_reset(struct sset *s) +{ + s->nbr = 0; +} + +static inline void sset_add(struct sset *s, unsigned int idx) +{ + unsigned int __idx = idx - s->off; + unsigned int n = s->nbr++; + s->sets[__idx] = n; + s->sets[s->size + n] = __idx; +} + +static inline bool sset_test(struct sset *s, unsigned int idx) +{ + unsigned int __idx = idx - s->off; + unsigned int n = s->sets[__idx]; + + return (n < s->nbr) && (s->sets[s->size + n] == __idx); +} + +static inline bool sset_testset(struct sset *s, unsigned int idx) +{ + if (sset_test(s, idx)) + return true; + sset_add(s, idx); + return false; +} + +#endif diff --git a/usr/src/tools/smatch/src/symbol.c b/usr/src/tools/smatch/src/symbol.c index bf3250e524..a590020f06 100644 --- a/usr/src/tools/smatch/src/symbol.c +++ b/usr/src/tools/smatch/src/symbol.c @@ -33,6 +33,7 @@ #include "symbol.h" #include "scope.h" #include "expression.h" +#include "evaluate.h" #include "target.h" @@ -48,9 +49,9 @@ struct symbol_list *translation_unit_used_list = NULL; void access_symbol(struct symbol *sym) { if (sym->ctype.modifiers & MOD_INLINE) { - if (!(sym->ctype.modifiers & MOD_ACCESSED)) { + if (!sym->accessed) { add_symbol(&translation_unit_used_list, sym); - sym->ctype.modifiers |= MOD_ACCESSED; + sym->accessed = 1; } } } @@ -213,7 +214,7 @@ static struct symbol *examine_base_type(struct symbol *sym) base_type = examine_symbol_type(sym->ctype.base_type); if (!base_type || base_type->type == SYM_PTR) return base_type; - sym->ctype.as |= base_type->ctype.as; + combine_address_space(sym->pos, &sym->ctype.as, base_type->ctype.as); sym->ctype.modifiers |= base_type->ctype.modifiers & MOD_PTRINHERIT; concat_ptr_list((struct ptr_list *)base_type->ctype.contexts, (struct ptr_list **)&sym->ctype.contexts); @@ -277,7 +278,7 @@ static struct symbol *examine_bitfield_type(struct symbol *sym) */ void merge_type(struct symbol *sym, struct symbol *base_type) { - sym->ctype.as |= base_type->ctype.as; + combine_address_space(sym->pos, &sym->ctype.as, base_type->ctype.as); sym->ctype.modifiers |= (base_type->ctype.modifiers & ~MOD_STORAGE); concat_ptr_list((struct ptr_list *)base_type->ctype.contexts, (struct ptr_list **)&sym->ctype.contexts); @@ -364,6 +365,23 @@ static struct expression *get_symbol_initializer(struct symbol *sym) return NULL; } +static unsigned int implicit_array_size(struct symbol *node, unsigned int count) +{ + struct symbol *arr_ori = node->ctype.base_type; + struct symbol *arr_new = alloc_symbol(node->pos, SYM_ARRAY); + struct symbol *elem_type = arr_ori->ctype.base_type; + struct expression *size = alloc_const_expression(node->pos, count); + unsigned int bit_size = array_element_offset(elem_type->bit_size, count); + + *arr_new = *arr_ori; + arr_new->bit_size = bit_size; + arr_new->array_size = size; + node->array_size = size; + node->ctype.base_type = arr_new; + + return bit_size; +} + static struct symbol * examine_node_type(struct symbol *sym) { struct symbol *base_type = examine_base_type(sym); @@ -393,7 +411,7 @@ static struct symbol * examine_node_type(struct symbol *sym) int count = count_array_initializer(node_type, initializer); if (node_type && node_type->bit_size >= 0) - bit_size = array_element_offset(node_type->bit_size, count); + bit_size = implicit_array_size(sym, count); } } @@ -479,13 +497,15 @@ struct symbol *examine_symbol_type(struct symbol * sym) sym->ctype.base_type = base; return examine_node_type(sym); } - break; + sym->type = SYM_NODE; + sym->ctype.base_type = &bad_ctype; + return sym; } case SYM_PREPROCESSOR: sparse_error(sym->pos, "ctype on preprocessor command? (%s)", show_ident(sym->ident)); return NULL; case SYM_UNINITIALIZED: -// sparse_error(sym->pos, "ctype on uninitialized symbol %p", sym); +// sparse_error(sym->pos, "ctype on uninitialized symbol '%s'", show_typename(sym)); return NULL; case SYM_RESTRICT: examine_base_type(sym); @@ -677,6 +697,14 @@ struct symbol bool_ctype, void_ctype, type_ctype, string_ctype, ptr_ctype, lazy_ptr_ctype, incomplete_ctype, label_ctype, bad_ctype, null_ctype; +struct symbol int_ptr_ctype, uint_ptr_ctype; +struct symbol long_ptr_ctype, ulong_ptr_ctype; +struct symbol llong_ptr_ctype, ullong_ptr_ctype; +struct symbol float32_ctype, float32x_ctype; +struct symbol float64_ctype, float64x_ctype; +struct symbol float128_ctype; +struct symbol const_void_ctype, const_char_ctype; +struct symbol const_ptr_ctype, const_string_ctype; struct symbol zero_int; @@ -703,6 +731,10 @@ void init_symbols(void) #else #define CHAR_SIGNEDNESS MOD_SIGNED #endif +// For fix-sized types +static int bits_in_type32 = 32; +static int bits_in_type64 = 64; +static int bits_in_type128 = 128; #define MOD_ESIGNED (MOD_SIGNED | MOD_EXPLICITLY_SIGNED) #define MOD_LL (MOD_LONG | MOD_LONGLONG) @@ -744,11 +776,28 @@ static const struct ctype_declare { { &double_ctype, SYM_BASETYPE, MOD_LONG, &bits_in_double, &max_fp_alignment, &fp_type }, { &ldouble_ctype, SYM_BASETYPE, MOD_LONG | MOD_LONGLONG, &bits_in_longdouble, &max_fp_alignment, &fp_type }, + { &float32_ctype, SYM_BASETYPE, 0, &bits_in_type32, &max_fp_alignment, &fp_type }, + { &float32x_ctype, SYM_BASETYPE, MOD_LONG, &bits_in_double, &max_fp_alignment, &fp_type }, + { &float64_ctype, SYM_BASETYPE, 0, &bits_in_type64, &max_fp_alignment, &fp_type }, + { &float64x_ctype, SYM_BASETYPE, MOD_LONG | MOD_LONGLONG, &bits_in_longdouble, &max_fp_alignment, &fp_type }, + { &float128_ctype, SYM_BASETYPE, 0, &bits_in_type128, &max_alignment, &fp_type }, + { &string_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &char_ctype }, { &ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &void_ctype }, { &null_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &void_ctype }, { &label_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &void_ctype }, { &lazy_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &void_ctype }, + { &int_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &int_ctype }, + { &uint_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &uint_ctype }, + { &long_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &long_ctype }, + { &ulong_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &ulong_ctype }, + { &llong_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &llong_ctype }, + { &ullong_ptr_ctype,SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &ullong_ctype }, + + { &const_void_ctype, SYM_NODE, MOD_CONST, NULL, NULL, &void_ctype }, + { &const_char_ctype, SYM_NODE, MOD_CONST, &bits_in_char, &max_int_alignment, &char_ctype }, + { &const_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &const_void_ctype }, + { &const_string_ctype,SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &const_char_ctype }, { NULL, } }; #undef MOD_LLL @@ -773,4 +822,10 @@ void init_ctype(void) sym->ctype.base_type = ctype->base_type; sym->ctype.modifiers = ctype->modifiers; } + + // and now some adjustments + if (funsigned_char) { + char_ctype.ctype.modifiers |= MOD_UNSIGNED; + char_ctype.ctype.modifiers &= ~MOD_SIGNED; + } } diff --git a/usr/src/tools/smatch/src/symbol.h b/usr/src/tools/smatch/src/symbol.h index 88959603c8..5c84516ecb 100644 --- a/usr/src/tools/smatch/src/symbol.h +++ b/usr/src/tools/smatch/src/symbol.h @@ -101,7 +101,7 @@ struct ctype { unsigned long modifiers; unsigned long alignment; struct context_list *contexts; - unsigned int as; + struct ident *as; struct symbol *base_type; }; @@ -110,6 +110,7 @@ struct decl_state { struct ident **ident; struct symbol_op *mode; unsigned char prefer_abstract, is_inline, storage_class, is_tls; + unsigned char is_ext_visible; }; struct symbol_op { @@ -124,6 +125,7 @@ struct symbol_op { struct token *(*toplevel)(struct token *token, struct symbol_list **list); struct token *(*attribute)(struct token *token, struct symbol *attr, struct decl_state *ctx); struct symbol *(*to_mode)(struct symbol *); + void (*asm_modifier)(struct token *token, unsigned long *mods); int test, set, class; }; @@ -155,6 +157,7 @@ struct symbol { struct token *expansion; struct token *arglist; struct scope *used_in; + void (*expander)(struct token *); }; struct /* NS_PREPROCESSOR */ { int (*handler)(struct stream *, struct token **, struct token *); @@ -164,7 +167,6 @@ struct symbol { unsigned long offset; int bit_size; unsigned int bit_offset:8, - arg_count:10, variadic:1, initialized:1, examined:1, @@ -173,6 +175,9 @@ struct symbol { string:1, designated_init:1, forced_arg:1, + accessed:1, + builtin:1, + torename:1, transparent_union:1; struct expression *array_size; struct ctype ctype; @@ -183,7 +188,6 @@ struct symbol { struct symbol_list *inline_symbol_list; struct expression *initializer; struct entrypoint *ep; - long long value; /* Initial value */ struct symbol *definition; }; }; @@ -199,55 +203,56 @@ struct symbol { }; /* Modifiers */ -#define MOD_AUTO 0x0001 -#define MOD_REGISTER 0x0002 -#define MOD_STATIC 0x0004 -#define MOD_EXTERN 0x0008 - -#define MOD_CONST 0x0010 -#define MOD_VOLATILE 0x0020 -#define MOD_SIGNED 0x0040 -#define MOD_UNSIGNED 0x0080 - -#define MOD_CHAR 0x0100 -#define MOD_SHORT 0x0200 -#define MOD_LONG 0x0400 -#define MOD_LONGLONG 0x0800 -#define MOD_LONGLONGLONG 0x1000 -#define MOD_PURE 0x2000 - -#define MOD_TYPEDEF 0x10000 - -#define MOD_TLS 0x20000 -#define MOD_INLINE 0x40000 -#define MOD_ADDRESSABLE 0x80000 - -#define MOD_NOCAST 0x100000 -#define MOD_NODEREF 0x200000 -#define MOD_ACCESSED 0x400000 -#define MOD_TOPLEVEL 0x800000 // scoping.. - -#define MOD_ASSIGNED 0x2000000 -#define MOD_TYPE 0x4000000 -#define MOD_SAFE 0x8000000 // non-null/non-trapping pointer - -#define MOD_USERTYPE 0x10000000 -#define MOD_NORETURN 0x20000000 -#define MOD_EXPLICITLY_SIGNED 0x40000000 -#define MOD_BITWISE 0x80000000 - - +#define MOD_AUTO 0x00000001 +#define MOD_REGISTER 0x00000002 +#define MOD_STATIC 0x00000004 +#define MOD_EXTERN 0x00000008 +#define MOD_TOPLEVEL 0x00000010 // scoping.. +#define MOD_TLS 0x00000020 +#define MOD_ASM_GOTO MOD_TLS // never used together +#define MOD_INLINE 0x00000040 + +#define MOD_ASSIGNED 0x00000080 +#define MOD_ADDRESSABLE 0x00000100 + +#define MOD_CONST 0x00000200 +#define MOD_VOLATILE 0x00000400 +#define MOD_RESTRICT 0x00000800 +#define MOD_ATOMIC 0x00001000 + +#define MOD_SIGNED 0x00002000 +#define MOD_UNSIGNED 0x00004000 +#define MOD_EXPLICITLY_SIGNED 0x00008000 + +#define MOD_TYPE 0x00010000 +#define MOD_USERTYPE 0x00020000 +#define MOD_CHAR 0x00040000 +#define MOD_SHORT 0x00080000 +#define MOD_LONG 0x00100000 +#define MOD_LONGLONG 0x00200000 +#define MOD_LONGLONGLONG 0x00400000 + +#define MOD_SAFE 0x00800000 // non-null/non-trapping pointer +#define MOD_PURE 0x01000000 +#define MOD_BITWISE 0x02000000 +#define MOD_NOCAST 0x04000000 +#define MOD_NODEREF 0x08000000 +#define MOD_NORETURN 0x10000000 +#define MOD_EXT_VISIBLE 0x20000000 + + +#define MOD_ACCESS (MOD_ASSIGNED | MOD_ADDRESSABLE) #define MOD_NONLOCAL (MOD_EXTERN | MOD_TOPLEVEL) #define MOD_STORAGE (MOD_AUTO | MOD_REGISTER | MOD_STATIC | MOD_EXTERN | MOD_INLINE | MOD_TOPLEVEL) #define MOD_SIGNEDNESS (MOD_SIGNED | MOD_UNSIGNED | MOD_EXPLICITLY_SIGNED) #define MOD_LONG_ALL (MOD_LONG | MOD_LONGLONG | MOD_LONGLONGLONG) #define MOD_SPECIFIER (MOD_CHAR | MOD_SHORT | MOD_LONG_ALL | MOD_SIGNEDNESS) #define MOD_SIZE (MOD_CHAR | MOD_SHORT | MOD_LONG_ALL) -#define MOD_IGNORE (MOD_TOPLEVEL | MOD_STORAGE | MOD_ADDRESSABLE | \ - MOD_ASSIGNED | MOD_USERTYPE | MOD_ACCESSED | MOD_EXPLICITLY_SIGNED) -#define MOD_PTRINHERIT (MOD_VOLATILE | MOD_CONST | MOD_NODEREF | MOD_NORETURN | MOD_NOCAST) +#define MOD_IGNORE (MOD_STORAGE | MOD_ACCESS | MOD_USERTYPE | MOD_EXPLICITLY_SIGNED | MOD_EXT_VISIBLE) +#define MOD_QUALIFIER (MOD_CONST | MOD_VOLATILE | MOD_RESTRICT | MOD_ATOMIC) +#define MOD_PTRINHERIT (MOD_QUALIFIER | MOD_NODEREF | MOD_NORETURN | MOD_NOCAST) /* modifiers preserved by typeof() operator */ -#define MOD_TYPEOF (MOD_VOLATILE | MOD_CONST | MOD_NOCAST | MOD_SPECIFIER) +#define MOD_TYPEOF (MOD_QUALIFIER | MOD_NOCAST | MOD_SPECIFIER) /* Current parsing/evaluation function */ @@ -269,6 +274,17 @@ extern struct symbol bool_ctype, void_ctype, type_ctype, string_ctype, ptr_ctype, lazy_ptr_ctype, incomplete_ctype, label_ctype, bad_ctype, null_ctype; +extern struct symbol int_ptr_ctype, uint_ptr_ctype; +extern struct symbol long_ptr_ctype, ulong_ptr_ctype; +extern struct symbol llong_ptr_ctype, ullong_ptr_ctype; +extern struct symbol float32_ctype, float32x_ctype; +extern struct symbol float64_ctype, float64x_ctype; +extern struct symbol float128_ctype; +extern struct symbol const_void_ctype, const_char_ctype; +extern struct symbol const_ptr_ctype, const_string_ctype; + +#define uintptr_ctype size_t_ctype +#define intptr_ctype ssize_t_ctype /* Special internal symbols */ extern struct symbol zero_int; @@ -277,7 +293,6 @@ extern struct symbol zero_int; extern struct ident n #include "ident-list.h" -#define symbol_is_typename(sym) ((sym)->type == SYM_TYPE) extern struct symbol_list *translation_unit_used_list; @@ -290,7 +305,9 @@ extern struct symbol *lookup_symbol(struct ident *, enum namespace); extern struct symbol *create_symbol(int stream, const char *name, int type, int namespace); extern void init_symbols(void); extern void init_builtins(int stream); +extern void declare_builtins(void); extern void init_ctype(void); +extern void init_target(void); extern struct symbol *alloc_symbol(struct position, int type); extern void show_type(struct symbol *); extern const char *modifier_string(unsigned long mod); @@ -303,21 +320,33 @@ extern void bind_symbol(struct symbol *, struct ident *, enum namespace); extern struct symbol *examine_symbol_type(struct symbol *); extern struct symbol *examine_pointer_target(struct symbol *); -extern void examine_simple_symbol_type(struct symbol *); +extern const char *show_as(struct ident *as); extern const char *show_typename(struct symbol *sym); extern const char *builtin_typename(struct symbol *sym); +extern const char *builtin_type_suffix(struct symbol *sym); extern const char *builtin_ctypename(struct ctype *ctype); extern const char* get_type_name(enum type type); extern void debug_symbol(struct symbol *); extern void merge_type(struct symbol *sym, struct symbol *base_type); extern void check_declaration(struct symbol *sym); +extern void check_duplicates(struct symbol *sym); + +static inline int valid_type(const struct symbol *ctype) +{ + return ctype && ctype != &bad_ctype; +} static inline struct symbol *get_base_type(const struct symbol *sym) { return examine_symbol_type(sym->ctype.base_type); } +/// +// test if type is an integer type +// +// @return: ``1`` for plain integer type, enums & bitfields +// but ``0`` for bitwise types! static inline int is_int_type(const struct symbol *type) { if (type->type == SYM_NODE) @@ -335,6 +364,15 @@ static inline int is_enum_type(const struct symbol *type) return (type->type == SYM_ENUM); } +static inline int is_signed_type(struct symbol *sym) +{ + if (sym->type == SYM_NODE) + sym = sym->ctype.base_type; + if (sym->type == SYM_PTR) + return 0; + return !(sym->ctype.modifiers & MOD_UNSIGNED); +} + static inline int is_type_type(struct symbol *type) { return (type->ctype.modifiers & MOD_TYPE) != 0; @@ -409,6 +447,24 @@ static inline int is_scalar_type(struct symbol *type) return 0; } +/// return true for integer & pointer types +static inline bool is_integral_type(struct symbol *type) +{ + if (type->type == SYM_NODE) + type = type->ctype.base_type; + switch (type->type) { + case SYM_ENUM: + case SYM_PTR: + case SYM_RESTRICT: // OK, always integer types + return 1; + default: + break; + } + if (type->ctype.base_type == &int_type) + return 1; + return 0; +} + static inline int is_function(struct symbol *type) { return type && type->type == SYM_FN; @@ -430,6 +486,14 @@ static inline int get_sym_type(struct symbol *type) return type->type; } +static inline long long extend_value(long long val, struct symbol *ctype) +{ + int is_signed = !(ctype->ctype.modifiers & MOD_UNSIGNED); + unsigned size = ctype->bit_size; + + return bits_extend(val, size, is_signed); +} + static inline struct symbol *lookup_keyword(struct ident *ident, enum namespace ns) { if (!ident->keyword) @@ -440,9 +504,31 @@ static inline struct symbol *lookup_keyword(struct ident *ident, enum namespace #define is_restricted_type(type) (get_sym_type(type) == SYM_RESTRICT) #define is_fouled_type(type) (get_sym_type(type) == SYM_FOULED) #define is_bitfield_type(type) (get_sym_type(type) == SYM_BITFIELD) -extern int is_ptr_type(struct symbol *); void create_fouled(struct symbol *type); struct symbol *befoul(struct symbol *type); + +extern struct ident bad_address_space; + +static inline bool valid_as(struct ident *as) +{ + return as && as != &bad_address_space; +} + +static inline void combine_address_space(struct position pos, + struct ident **tas, struct ident *sas) +{ + struct ident *as; + if (!sas) + return; + as = *tas; + if (!as) + *tas = sas; + else if (as != sas) { + *tas = &bad_address_space; + sparse_error(pos, "multiple address spaces given"); + } +} + #endif /* SYMBOL_H */ diff --git a/usr/src/tools/smatch/src/target.c b/usr/src/tools/smatch/src/target.c index 40a9bf1eac..97733ba649 100644 --- a/usr/src/tools/smatch/src/target.c +++ b/usr/src/tools/smatch/src/target.c @@ -2,9 +2,18 @@ #include "symbol.h" #include "target.h" +#include "machine.h" struct symbol *size_t_ctype = &ulong_ctype; struct symbol *ssize_t_ctype = &long_ctype; +struct symbol *intmax_ctype = &llong_ctype; +struct symbol *uintmax_ctype = &ullong_ctype; +struct symbol *int64_ctype = &long_ctype; +struct symbol *uint64_ctype = &ulong_ctype; +struct symbol *int32_ctype = &int_ctype; +struct symbol *uint32_ctype = &uint_ctype; +struct symbol *wchar_ctype = &int_ctype; +struct symbol *wint_ctype = &uint_ctype; /* * For "__attribute__((aligned))" @@ -22,8 +31,6 @@ int bits_in_long = 32; int bits_in_longlong = 64; int bits_in_longlonglong = 128; -int bits_in_wchar = 32; - int max_int_alignment = 4; /* @@ -31,9 +38,9 @@ int max_int_alignment = 4; */ int bits_in_float = 32; int bits_in_double = 64; -int bits_in_longdouble = 80; +int bits_in_longdouble = 128; -int max_fp_alignment = 8; +int max_fp_alignment = 16; /* * Pointer data type @@ -46,3 +53,111 @@ int pointer_alignment = 4; */ int bits_in_enum = 32; int enum_alignment = 4; + + +void init_target(void) +{ + switch (arch_mach) { + case MACH_X86_64: + if (arch_m64 == ARCH_LP64) + break; + /* fall through */ + case MACH_I386: + case MACH_M68K: + case MACH_SPARC32: + case MACH_PPC32: + wchar_ctype = &long_ctype; + break; + case MACH_ARM: + case MACH_ARM64: + wchar_ctype = &uint_ctype; + break; + default: + break; + } + + switch (arch_mach) { + case MACH_MIPS64: + if (arch_m64 == ARCH_LP64) + break; + /* fall through */ + case MACH_M68K: + case MACH_SPARC32: + case MACH_PPC32: + case MACH_MIPS32: + case MACH_RISCV32: + arch_m64 = ARCH_LP32; + int32_ctype = &long_ctype; + uint32_ctype = &ulong_ctype; + break; + default: + break; + } + + switch (arch_mach) { + case MACH_ARM: + case MACH_MIPS32: + case MACH_S390X: + case MACH_SPARC32: + bits_in_longdouble = 64; + max_fp_alignment = 8; + break; + case MACH_X86_64: + if (arch_m64 == ARCH_LP64 || arch_m64 == ARCH_X32) + break; + /* fall through */ + case MACH_I386: + case MACH_M68K: + bits_in_longdouble = 96; + max_fp_alignment = 4; + break; + default: + break; + } + + switch (arch_m64) { + case ARCH_X32: + max_int_alignment = 8; + int64_ctype = &llong_ctype; + uint64_ctype = &ullong_ctype; + break; + case ARCH_LP32: + /* default values */ + int64_ctype = &llong_ctype; + uint64_ctype = &ullong_ctype; + intmax_ctype = &llong_ctype; + uintmax_ctype = &ullong_ctype; + break; + case ARCH_LP64: + bits_in_long = 64; + max_int_alignment = 8; + size_t_ctype = &ulong_ctype; + ssize_t_ctype = &long_ctype; + intmax_ctype = &long_ctype; + uintmax_ctype = &ulong_ctype; + goto case_64bit_common; + case ARCH_LLP64: + bits_in_long = 32; + max_int_alignment = 8; + size_t_ctype = &ullong_ctype; + ssize_t_ctype = &llong_ctype; + int64_ctype = &llong_ctype; + uint64_ctype = &ullong_ctype; + goto case_64bit_common; + case_64bit_common: + bits_in_pointer = 64; + pointer_alignment = 8; + break; + } + +#if defined(__CYGWIN__) + wchar_ctype = &ushort_ctype; +#endif +#if defined(__FreeBSD__) || defined(__APPLE__) + wint_ctype = &int_ctype; +#endif +#if defined(__APPLE__) + int64_ctype = &llong_ctype; + uint64_ctype = &ullong_ctype; +#endif +} diff --git a/usr/src/tools/smatch/src/target.h b/usr/src/tools/smatch/src/target.h index 8326fa21a4..8bbe494f89 100644 --- a/usr/src/tools/smatch/src/target.h +++ b/usr/src/tools/smatch/src/target.h @@ -3,6 +3,14 @@ extern struct symbol *size_t_ctype; extern struct symbol *ssize_t_ctype; +extern struct symbol *intmax_ctype; +extern struct symbol *uintmax_ctype; +extern struct symbol *int64_ctype; +extern struct symbol *uint64_ctype; +extern struct symbol *int32_ctype; +extern struct symbol *uint32_ctype; +extern struct symbol *wchar_ctype; +extern struct symbol *wint_ctype; /* * For "__attribute__((aligned))" @@ -20,8 +28,6 @@ extern int bits_in_long; extern int bits_in_longlong; extern int bits_in_longlonglong; -extern int bits_in_wchar; - extern int max_int_alignment; /* diff --git a/usr/src/tools/smatch/src/test-dissect.c b/usr/src/tools/smatch/src/test-dissect.c index 862318f81a..266148be0d 100644 --- a/usr/src/tools/smatch/src/test-dissect.c +++ b/usr/src/tools/smatch/src/test-dissect.c @@ -88,10 +88,10 @@ int main(int argc, char **argv) sparse_initialize(argc, argv, &filelist); - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { dotc_stream = input_stream_nr; dissect(__sparse(file), &reporter); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); return 0; } diff --git a/usr/src/tools/smatch/src/test-inspect.c b/usr/src/tools/smatch/src/test-inspect.c index e437e11242..63754cb3c3 100644 --- a/usr/src/tools/smatch/src/test-inspect.c +++ b/usr/src/tools/smatch/src/test-inspect.c @@ -32,11 +32,11 @@ int main(int argc, char **argv) gtk_init(&argc,&argv); expand_symbols(sparse_initialize(argc, argv, &filelist)); - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { struct symbol_list *syms = sparse(file); expand_symbols(syms); concat_symbol_list(syms, &view_syms); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); treeview_main(view_syms); return 0; } diff --git a/usr/src/tools/smatch/src/test-lexing.c b/usr/src/tools/smatch/src/test-lexing.c index 5c7ab2adfc..a263986355 100644 --- a/usr/src/tools/smatch/src/test-lexing.c +++ b/usr/src/tools/smatch/src/test-lexing.c @@ -41,9 +41,9 @@ int main(int argc, char **argv) preprocess_only = 1; sparse_initialize(argc, argv, &filelist); - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { sparse(file); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); show_identifier_stats(); return 0; } diff --git a/usr/src/tools/smatch/src/test-linearize.c b/usr/src/tools/smatch/src/test-linearize.c index fe0673befa..17ad5d5085 100644 --- a/usr/src/tools/smatch/src/test-linearize.c +++ b/usr/src/tools/smatch/src/test-linearize.c @@ -47,6 +47,8 @@ static void clean_up_symbols(struct symbol_list *list) expand_symbol(sym); ep = linearize_symbol(sym); + if (!(fdump_ir & PASS_FINAL)) + continue; if (ep) show_entry(ep); } END_FOR_EACH_PTR(sym); @@ -58,9 +60,9 @@ int main(int argc, char **argv) char *file; clean_up_symbols(sparse_initialize(argc, argv, &filelist)); - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { clean_up_symbols(sparse(file)); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); report_stats(); return 0; diff --git a/usr/src/tools/smatch/src/test-parsing.c b/usr/src/tools/smatch/src/test-parsing.c index 7642205af3..c5bc42e190 100644 --- a/usr/src/tools/smatch/src/test-parsing.c +++ b/usr/src/tools/smatch/src/test-parsing.c @@ -64,7 +64,7 @@ int main(int argc, char **argv) printf("\n\n"); #endif - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { list = sparse(file); // Simplification @@ -75,7 +75,7 @@ int main(int argc, char **argv) show_symbol_list(list, "\n\n"); printf("\n\n"); #endif - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); #if 0 // And show the allocation statistics diff --git a/usr/src/tools/smatch/src/test-unssa.c b/usr/src/tools/smatch/src/test-unssa.c index 240d996016..b81dc47bc0 100644 --- a/usr/src/tools/smatch/src/test-unssa.c +++ b/usr/src/tools/smatch/src/test-unssa.c @@ -12,7 +12,7 @@ static void output_bb(struct basic_block *bb, unsigned long generation) struct instruction *insn; bb->generation = generation; - printf(".L%u\n", bb->nr); + printf("%s\n", show_label(bb)); FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb) @@ -62,6 +62,8 @@ static int compile(struct symbol_list *list) struct entrypoint *ep; expand_symbol(sym); ep = linearize_symbol(sym); + if (!(fdump_ir & PASS_FINAL)) + continue; if (ep) output_fn(ep); else @@ -78,9 +80,9 @@ int main(int argc, char **argv) char *file; compile(sparse_initialize(argc, argv, &filelist)); - FOR_EACH_PTR_NOTAG(filelist, file) { + FOR_EACH_PTR(filelist, file) { compile(sparse(file)); - } END_FOR_EACH_PTR_NOTAG(file); + } END_FOR_EACH_PTR(file); report_stats(); return 0; diff --git a/usr/src/tools/smatch/src/token.h b/usr/src/tools/smatch/src/token.h index 3644613f58..88f60db597 100644 --- a/usr/src/tools/smatch/src/token.h +++ b/usr/src/tools/smatch/src/token.h @@ -80,6 +80,7 @@ struct ident { enum token_type { TOKEN_EOF, + TOKEN_BAD, TOKEN_ERROR, TOKEN_IDENT, TOKEN_ZERO_IDENT, @@ -238,6 +239,8 @@ extern char *pos_ident(struct position pos); extern void store_macro_pos(struct token *); extern char *get_macro_name(struct position pos); +extern char *get_inner_macro(struct position pos); +extern struct string_list *get_all_macros(struct position pos); static inline int match_op(struct token *token, unsigned int op) { diff --git a/usr/src/tools/smatch/src/tokenize.c b/usr/src/tools/smatch/src/tokenize.c index ff5b1367fb..aefd333b48 100644 --- a/usr/src/tools/smatch/src/tokenize.c +++ b/usr/src/tools/smatch/src/tokenize.c @@ -93,9 +93,13 @@ const char *show_special(int val) const char *show_ident(const struct ident *ident) { - static char buffer[256]; + static char buff[4][256]; + static int n; + char *buffer; + if (!ident) return "<noident>"; + buffer = buff[3 & ++n]; sprintf(buffer, "%.*s", ident->len, ident->name); return buffer; } @@ -129,7 +133,7 @@ const char *show_string(const struct string *string) char *ptr; int i; - if (!string->length) + if (!string || !string->length) return "<bad_string>"; ptr = buffer; *ptr++ = '"'; @@ -449,6 +453,7 @@ static struct token *mark_eof(stream_t *stream) struct token *end; end = alloc_token(stream); + eof_token_entry.pos = end->pos; token_type(end) = TOKEN_STREAMEND; end->pos.newline = 1; @@ -488,32 +493,20 @@ enum { Quote = 64, }; -static const long cclass[257] = { - ['0' + 1 ... '7' + 1] = Digit | Hex, /* \<octal> */ - ['8' + 1 ... '9' + 1] = Digit | Hex, +static const char cclass[257] = { + ['0' + 1 ... '9' + 1] = Digit | Hex, ['A' + 1 ... 'D' + 1] = Letter | Hex, ['E' + 1] = Letter | Hex | Exp, /* E<exp> */ ['F' + 1] = Letter | Hex, ['G' + 1 ... 'O' + 1] = Letter, ['P' + 1] = Letter | Exp, /* P<exp> */ ['Q' + 1 ... 'Z' + 1] = Letter, - ['a' + 1 ... 'b' + 1] = Letter | Hex, /* \a, \b */ - ['c' + 1 ... 'd' + 1] = Letter | Hex, - ['e' + 1] = Letter | Hex | Exp,/* \e, e<exp> */ - ['f' + 1] = Letter | Hex, /* \f */ - ['g' + 1 ... 'm' + 1] = Letter, - ['n' + 1] = Letter, /* \n */ - ['o' + 1] = Letter, + ['a' + 1 ... 'd' + 1] = Letter | Hex, + ['e' + 1] = Letter | Hex | Exp, /* e<exp> */ + ['f' + 1] = Letter | Hex, + ['g' + 1 ... 'o' + 1] = Letter, ['p' + 1] = Letter | Exp, /* p<exp> */ - ['q' + 1] = Letter, - ['r' + 1] = Letter, /* \r */ - ['s' + 1] = Letter, - ['t' + 1] = Letter, /* \t */ - ['u' + 1] = Letter, - ['v' + 1] = Letter, /* \v */ - ['w' + 1] = Letter, - ['x' + 1] = Letter, /* \x<hex> */ - ['y' + 1 ... 'z' + 1] = Letter, + ['q' + 1 ... 'z' + 1] = Letter, ['_' + 1] = Letter, ['.' + 1] = Dot | ValidSecond, ['=' + 1] = ValidSecond, @@ -544,8 +537,7 @@ static int get_one_number(int c, int next, stream_t *stream) { struct token *token; static char buffer[4095]; - char *p = buffer, *buf, *buffer_end = buffer + sizeof (buffer); - int len; + char *p = buffer, *buffer_end = buffer + sizeof (buffer); *p++ = c; for (;;) { @@ -573,13 +565,9 @@ static int get_one_number(int c, int next, stream_t *stream) } *p++ = 0; - len = p - buffer; - buf = __alloc_bytes(len); - memcpy(buf, buffer, len); - token = stream->token; token_type(token) = TOKEN_NUMBER; - token->number = buf; + token->number = xmemdup(buffer, p - buffer); add_token(stream); return next; @@ -601,9 +589,9 @@ static int eat_string(int next, stream_t *stream, enum token_type type) len++; if (next == '\n') { warning(stream_pos(stream), - "Newline in string or character constant"); - if (delim == '\'') /* assume it's lost ' */ - break; + "missing terminating %c character", delim); + /* assume delimiter is lost */ + break; } if (next == EOF) { warning(stream_pos(stream), diff --git a/usr/src/tools/smatch/src/unssa.c b/usr/src/tools/smatch/src/unssa.c index e7c9154d53..334ffa4704 100644 --- a/usr/src/tools/smatch/src/unssa.c +++ b/usr/src/tools/smatch/src/unssa.c @@ -34,11 +34,6 @@ #include <assert.h> -static inline int nbr_pseudo_users(pseudo_t p) -{ - return ptr_list_size((struct ptr_list *)p->users); -} - static int simplify_phi_node(struct instruction *phi, pseudo_t tmp) { pseudo_t target = phi->target; @@ -95,7 +90,7 @@ static void replace_phi_node(struct instruction *phi) src = def->phi_src; if (src->type != PSEUDO_REG) continue; - switch (nbr_pseudo_users(src)) { + switch (nbr_users(src)) { struct instruction *insn; case 1: insn = src->def; diff --git a/usr/src/tools/smatch/src/utils.c b/usr/src/tools/smatch/src/utils.c new file mode 100644 index 0000000000..094df3f9bf --- /dev/null +++ b/usr/src/tools/smatch/src/utils.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT +// Copyright (C) 2018 Luc Van Oostenryck + +#include "utils.h" +#include "allocate.h" +#include <string.h> +#include <stdarg.h> +#include <stdio.h> + + +void *xmemdup(const void *src, size_t len) +{ + return memcpy(__alloc_bytes(len), src, len); +} + +char *xstrdup(const char *src) +{ + return xmemdup(src, strlen(src) + 1); +} + +char *xvasprintf(const char *fmt, va_list ap) +{ + va_list ap2; + char *str; + int n; + + va_copy(ap2, ap); + n = vsnprintf(NULL, 0, fmt, ap2) + 1; + va_end(ap2); + + str = __alloc_bytes(n); + vsnprintf(str, n, fmt, ap); + + return str; +} + +char *xasprintf(const char *fmt, ...) +{ + va_list ap; + char *str; + + va_start(ap, fmt); + str = xvasprintf(fmt, ap); + va_end(ap); + + return str; +} diff --git a/usr/src/tools/smatch/src/utils.h b/usr/src/tools/smatch/src/utils.h new file mode 100644 index 0000000000..7bd14f4677 --- /dev/null +++ b/usr/src/tools/smatch/src/utils.h @@ -0,0 +1,43 @@ +#ifndef UTILS_H +#define UTILS_H + +/// +// Miscellaneous utilities +// ----------------------- + +#include <stddef.h> +#include <stdarg.h> + +/// +// duplicate a memory buffer in a newly allocated buffer. +// @src: a pointer to the memory buffer to be duplicated +// @len: the size of the memory buffer to be duplicated +// @return: a pointer to a copy of @src allocated via +// :func:`__alloc_bytes()`. +void *xmemdup(const void *src, size_t len); + +/// +// duplicate a null-terminated string in a newly allocated buffer. +// @src: a pointer to string to be duplicated +// @return: a pointer to a copy of @str allocated via +// :func:`__alloc_bytes()`. +char *xstrdup(const char *src); + +/// +// printf to allocated string +// @fmt: the format followed by its arguments. +// @return: the allocated & formatted string. +// This function is similar to asprintf() but the resulting string +// is allocated with __alloc_bytes(). +char *xasprintf(const char *fmt, ...); + +/// +// vprintf to allocated string +// @fmt: the format +// @ap: the variadic arguments +// @return: the allocated & formatted string. +// This function is similar to asprintf() but the resulting string +// is allocated with __alloc_bytes(). +char *xvasprintf(const char *fmt, va_list ap); + +#endif diff --git a/usr/src/tools/smatch/src/validation/Waddress-array.c b/usr/src/tools/smatch/src/validation/Waddress-array.c new file mode 100644 index 0000000000..8317845481 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/Waddress-array.c @@ -0,0 +1,25 @@ +int foo(void) { + extern int a[]; + + if (a) + return 1; + return 0; +} + +int bar(void) { + int a[2]; + + if (a) + return 1; + return 0; +} + +/* + * check-name: Waddress-array + * check-command: sparse -Wno-decl -Waddress $file + * + * check-error-start +Waddress-array.c:4:13: warning: the address of an array will always evaluate as true +Waddress-array.c:12:13: warning: the address of an array will always evaluate as true + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/Waddress-function.c b/usr/src/tools/smatch/src/validation/Waddress-function.c new file mode 100644 index 0000000000..fccbe2d89c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/Waddress-function.c @@ -0,0 +1,17 @@ +extern void func(void); + +int global_function(void) +{ + if (func) + return 1; + return 0; +} + +/* + * check-name: Waddress-function + * check-command: sparse -Wno-decl -Waddress $file + * + * check-error-start +Waddress-function.c:5:13: warning: the address of a function will always evaluate as true + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/Waddress-space-all-attr.c b/usr/src/tools/smatch/src/validation/Waddress-space-all-attr.c new file mode 100644 index 0000000000..b0c17693ae --- /dev/null +++ b/usr/src/tools/smatch/src/validation/Waddress-space-all-attr.c @@ -0,0 +1,64 @@ +/* Resembles include/linux/compiler_types.h */ +#define __kernel __attribute__((address_space(0))) +#define __user __attribute__((address_space(1))) +#define __iomem __attribute__((address_space(2))) +#define __percpu __attribute__((address_space(3))) +#define __rcu __attribute__((address_space(4))) + + +typedef unsigned long ulong; +typedef struct s obj_t; + +static void expl(obj_t __kernel *k, obj_t __iomem *o, + obj_t __user *p, obj_t __percpu *pc, + obj_t __rcu *r) +{ + (ulong)(k); (__UINTPTR_TYPE__)(k); + (void *)(k); + (obj_t*)(k); + (obj_t __kernel*)(k); + + (ulong)(o); (__UINTPTR_TYPE__)(o); + (void *)(o); + (obj_t*)(o); + (obj_t __iomem*)(o); + + (ulong)(p); (__UINTPTR_TYPE__)(p); + (void *)(p); + (obj_t*)(p); + (obj_t __user*)(p); + + (ulong)(pc); (__UINTPTR_TYPE__)(pc); + (void *)(pc); + (obj_t*)(pc); + (obj_t __percpu*)(pc); + + (ulong)(r); (__UINTPTR_TYPE__)(r); + (void *)(r); + (obj_t*)(r); + (obj_t __rcu*)(r); +} + +/* + * check-name: Waddress-space-all-attr + * check-command: sparse -Wcast-from-as -Wcast-to-as $file + * + * check-error-start +Waddress-space-all-attr.c:21:10: warning: cast removes address space '<asn:2>' of expression +Waddress-space-all-attr.c:21:22: warning: cast removes address space '<asn:2>' of expression +Waddress-space-all-attr.c:22:10: warning: cast removes address space '<asn:2>' of expression +Waddress-space-all-attr.c:23:10: warning: cast removes address space '<asn:2>' of expression +Waddress-space-all-attr.c:26:10: warning: cast removes address space '<asn:1>' of expression +Waddress-space-all-attr.c:26:22: warning: cast removes address space '<asn:1>' of expression +Waddress-space-all-attr.c:27:10: warning: cast removes address space '<asn:1>' of expression +Waddress-space-all-attr.c:28:10: warning: cast removes address space '<asn:1>' of expression +Waddress-space-all-attr.c:31:10: warning: cast removes address space '<asn:3>' of expression +Waddress-space-all-attr.c:31:23: warning: cast removes address space '<asn:3>' of expression +Waddress-space-all-attr.c:32:10: warning: cast removes address space '<asn:3>' of expression +Waddress-space-all-attr.c:33:10: warning: cast removes address space '<asn:3>' of expression +Waddress-space-all-attr.c:36:10: warning: cast removes address space '<asn:4>' of expression +Waddress-space-all-attr.c:36:22: warning: cast removes address space '<asn:4>' of expression +Waddress-space-all-attr.c:37:10: warning: cast removes address space '<asn:4>' of expression +Waddress-space-all-attr.c:38:10: warning: cast removes address space '<asn:4>' of expression + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/Waddress-space-from.c b/usr/src/tools/smatch/src/validation/Waddress-space-from.c new file mode 100644 index 0000000000..317a205b6c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/Waddress-space-from.c @@ -0,0 +1,63 @@ + +#define __kernel __attribute__((address_space(0))) +#define __user __attribute__((address_space(__user))) +#define __iomem __attribute__((address_space(__iomem))) +#define __percpu __attribute__((address_space(__percpu))) +#define __rcu __attribute__((address_space(__rcu))) + + +typedef struct s obj_t; + +static void expl(obj_t __kernel *k, obj_t __iomem *o, + obj_t __user *p, obj_t __percpu *pc, + obj_t __rcu *r) +{ + (__UINTPTR_TYPE__)(k); // OK + (unsigned long)(k); // OK + (void *)(k); // OK + (obj_t*)(k); // OK + (obj_t __kernel*)(k); // OK + + (__UINTPTR_TYPE__)(o); // OK + (unsigned long)(o); // OK + (void *)(o); + (obj_t*)(o); + (obj_t __iomem*)(o); // OK + + (__UINTPTR_TYPE__)(p); // OK + (unsigned long)(p); // OK + (void *)(p); + (obj_t*)(p); + (obj_t __user*)(p); // OK + + (__UINTPTR_TYPE__)(pc); // OK + (unsigned long)(pc); // OK + (void *)(pc); + (obj_t*)(pc); + (obj_t __percpu*)(pc); // OK + + (__UINTPTR_TYPE__)(r); // OK + (unsigned long)(r); // OK + (void *)(r); + (obj_t*)(r); + (obj_t __rcu*)(r); // OK +} + +/* + * check-name: Waddress-space-from + * check-command: sparse -Wno-cast-from-as $file + * check-description: Test the removal of AS from a pointer but only + * in the non-strict variant where casts to ulong (or uintptr_t) + * are allowed. + * + * check-error-start +Waddress-space-from.c:23:10: warning: cast removes address space '__iomem' of expression +Waddress-space-from.c:24:10: warning: cast removes address space '__iomem' of expression +Waddress-space-from.c:29:10: warning: cast removes address space '__user' of expression +Waddress-space-from.c:30:10: warning: cast removes address space '__user' of expression +Waddress-space-from.c:35:10: warning: cast removes address space '__percpu' of expression +Waddress-space-from.c:36:10: warning: cast removes address space '__percpu' of expression +Waddress-space-from.c:41:10: warning: cast removes address space '__rcu' of expression +Waddress-space-from.c:42:10: warning: cast removes address space '__rcu' of expression + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/Waddress-space-strict.c b/usr/src/tools/smatch/src/validation/Waddress-space-strict.c new file mode 100644 index 0000000000..a1c5b2771e --- /dev/null +++ b/usr/src/tools/smatch/src/validation/Waddress-space-strict.c @@ -0,0 +1,36 @@ +#define __user __attribute__((address_space(1))) + +typedef unsigned long ulong; +typedef struct s obj_t; + +static void expl(ulong u, void *v, obj_t *o, obj_t __user *p) +{ + (obj_t*)(u); + (obj_t __user*)(u); + + (obj_t*)(v); + (obj_t __user*)(v); + + (ulong)(o); + (void *)(o); + (obj_t*)(o); + (obj_t __user*)(o); + + (ulong)(p); // w! + (void *)(p); // w + (obj_t*)(p); // w + (obj_t __user*)(p); // ok +} + +/* + * check-name: Waddress-space-strict + * check-command: sparse -Wcast-from-as -Wcast-to-as $file + * + * check-error-start +Waddress-space-strict.c:12:10: warning: cast adds address space '<asn:1>' to expression +Waddress-space-strict.c:17:10: warning: cast adds address space '<asn:1>' to expression +Waddress-space-strict.c:19:10: warning: cast removes address space '<asn:1>' of expression +Waddress-space-strict.c:20:10: warning: cast removes address space '<asn:1>' of expression +Waddress-space-strict.c:21:10: warning: cast removes address space '<asn:1>' of expression + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/Waddress-weak.c b/usr/src/tools/smatch/src/validation/Waddress-weak.c new file mode 100644 index 0000000000..ad2cb13abb --- /dev/null +++ b/usr/src/tools/smatch/src/validation/Waddress-weak.c @@ -0,0 +1,27 @@ +extern int var __attribute__((weak)); +extern int arr[] __attribute__((weak)); +extern int fun(void) __attribute__((weak)); + +int test_addr_weak_fun(void) +{ + if ( &var) return 1; + if ( arr) return 1; + if ( &arr) return 1; + if ( fun) return 1; + if ( &fun) return 1; + if ( *fun) return 1; + if (!&var) return 0; + if (! arr) return 0; + if (!&arr) return 0; + if (! fun) return 0; + if (!&fun) return 0; + if (!*fun) return 0; + return -1; +} + +/* + * check-name: Waddress-weak + * check-note: Undefined weak symbols (can) have a null address. + * check-command: sparse -Wno-decl -Waddress $file + * check-known-to-fail + */ diff --git a/usr/src/tools/smatch/src/validation/Waddress.c b/usr/src/tools/smatch/src/validation/Waddress.c new file mode 100644 index 0000000000..441cdb1cbc --- /dev/null +++ b/usr/src/tools/smatch/src/validation/Waddress.c @@ -0,0 +1,114 @@ +extern int fun(void); +extern int arr[]; +extern int var; + +int test_address(int arg, int ptr[]) +{ + + if (fun()) return -1; + if (var) return -1; + if (arg) return -1; + if (ptr) return -1; + +lab: + if (arr) return 1; + if (&arr) return 1; + if (fun) return 1; + if (&fun) return 1; + if (*fun) return 1; + if (&var) return 1; + if (&arg) return 1; + if (&&lab) return 1; + + return -1; +} + +int test_address_not(int arg, int ptr[]) +{ + + if (!fun()) return -1; + if (!var) return -1; + if (!arg) return -1; + if (!ptr) return -1; + +lab: + if (!arr) return 0; + if (!&arr) return 0; + if (!fun) return 0; + if (!&fun) return 0; + if (!*fun) return 0; + if (!&var) return 0; + if (!&arg) return 0; + if (!&&lab) return 0; + + return -1; +} + +int test_address_cmp(int arg, int ptr[]) +{ + if (fun() == 0) return -1; + if (0 == fun()) return -1; + if (var == 0) return -1; + if (0 == var) return -1; + if (arg == 0) return -1; + if (0 == arg) return -1; + if (ptr == 0) return -1; + if (0 == ptr) return -1; + +lab: + if (arr == 0) return 0; + if (0 == arr) return 0; + if (&arr == 0) return 0; + if (0 == &arr) return 0; + if (fun == 0) return 0; + if (0 == fun) return 0; + if (&fun == 0) return 0; + if (0 == &fun) return 0; + if (*fun == 0) return 0; + if (0 == *fun) return 0; + if (&var == 0) return 0; + if (0 == &var) return 0; + if (&arg == 0) return 0; + if (0 == &arg) return 0; + if (&&lab == 0) return 0; + if (0 == &&lab) return 0; + + return -1; +} + +/* + * check-name: Waddress + * check-command: sparse -Wno-decl -Wno-non-pointer-null -Waddress $file + * check-known-to-fail + * + * check-error-start +Waddress.c:14:13: warning: the address of an array will always evaluate as true +Waddress.c:15:14: warning: the address of an array will always evaluate as true +Waddress.c:16:13: warning: the address of a function will always evaluate as true +Waddress.c:17:14: warning: the address of a function will always evaluate as true +Waddress.c:18:13: warning: the address of a variable will always evaluate as true +Waddress.c:19:13: warning: the address of a variable will always evaluate as true +Waddress.c:20:13: warning: the address of a label will always evaluate as true +Waddress.c:34:13: warning: the address of an array will always evaluate as true +Waddress.c:35:13: warning: the address of an array will always evaluate as true +Waddress.c:36:13: warning: the address of a function will always evaluate as true +Waddress.c:37:13: warning: the address of a function will always evaluate as true +Waddress.c:38:13: warning: the address of a variable will always evaluate as true +Waddress.c:39:13: warning: the address of a variable will always evaluate as true +Waddress.c:40:13: warning: the address of a label will always evaluate as true +Waddress.c:57:13: warning: the address of an array will always evaluate as true +Waddress.c:58:13: warning: the address of an array will always evaluate as true +Waddress.c:59:13: warning: the address of an array will always evaluate as true +Waddress.c:60:13: warning: the address of an array will always evaluate as true +Waddress.c:61:13: warning: the address of a function will always evaluate as true +Waddress.c:62:13: warning: the address of a function will always evaluate as true +Waddress.c:63:13: warning: the address of a function will always evaluate as true +Waddress.c:64:13: warning: the address of a function will always evaluate as true +Waddress.c:65:13: warning: the address of a variable will always evaluate as true +Waddress.c:66:13: warning: the address of a variable will always evaluate as true +Waddress.c:67:13: warning: the address of a variable will always evaluate as true +Waddress.c:68:13: warning: the address of a variable will always evaluate as true +Waddress.c:69:13: warning: the address of a label will always evaluate as true +Waddress.c:70:13: warning: the address of a label will always evaluate as true + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/Wcast-to-as.c b/usr/src/tools/smatch/src/validation/Wcast-to-as.c new file mode 100644 index 0000000000..8c51209102 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/Wcast-to-as.c @@ -0,0 +1,36 @@ +#define __user __attribute__((address_space(1))) + +typedef __UINTPTR_TYPE__ uintptr_t; +typedef unsigned long ulong; +typedef struct s obj_t; + +static void expl(ulong u, uintptr_t uip, void *v, obj_t *o, obj_t __user *p) +{ + (obj_t*)(u); + (obj_t __user*)(u); + + (obj_t*)(uip); + (obj_t __user*)(uip); + + (obj_t*)(v); + (obj_t __user*)(v); + + (ulong)(o); + (void *)(o); + (obj_t*)(o); + (obj_t __user*)(o); + + (ulong)(p); + (obj_t __user*)(p); + +} + +/* + * check-name: cast-to-as + * check-command: sparse -Wcast-to-as $file + * + * check-error-start +Wcast-to-as.c:16:10: warning: cast adds address space '<asn:1>' to expression +Wcast-to-as.c:21:10: warning: cast adds address space '<asn:1>' to expression + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/Wexternal-function-has-definition.c b/usr/src/tools/smatch/src/validation/Wexternal-function-has-definition.c new file mode 100644 index 0000000000..fdcaa19e10 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/Wexternal-function-has-definition.c @@ -0,0 +1,12 @@ + +extern int myfunction(void); + +extern int myfunction(void) { return 0; } + +/* + * check-name: external-function-has-definition + * check-command: sparse -Wno-external-function-has-definition $file + * + * check-error-start + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/Wunknown-attribute-def.c b/usr/src/tools/smatch/src/validation/Wunknown-attribute-def.c index 0c0868d6ba..cccce35780 100644 --- a/usr/src/tools/smatch/src/validation/Wunknown-attribute-def.c +++ b/usr/src/tools/smatch/src/validation/Wunknown-attribute-def.c @@ -2,8 +2,4 @@ static int foo(void) __attribute__((unknown_attribute)); /* * check-name: warn-unknown-attribute - * - * check-error-start -Wunknown-attribute-def.c:1:37: warning: attribute 'unknown_attribute': unknown attribute - * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/Wunknown-attribute-yes.c b/usr/src/tools/smatch/src/validation/Wunknown-attribute-yes.c index 72538cf5d5..4b7fcc32ba 100644 --- a/usr/src/tools/smatch/src/validation/Wunknown-attribute-yes.c +++ b/usr/src/tools/smatch/src/validation/Wunknown-attribute-yes.c @@ -5,6 +5,6 @@ static int foo(void) __attribute__((unknown_attribute)); * check-command: sparse -Wunknown-attribute $file * * check-error-start -Wunknown-attribute-yes.c:1:37: warning: attribute 'unknown_attribute': unknown attribute +Wunknown-attribute-yes.c:1:37: warning: unknown attribute 'unknown_attribute' * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/abi-integer.c b/usr/src/tools/smatch/src/validation/abi-integer.c new file mode 100644 index 0000000000..441b209450 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/abi-integer.c @@ -0,0 +1,31 @@ +#define TEST(T, S, A) \ + _Static_assert(sizeof(T) == S && _Alignof(T) == A, #T) + +int main(void) +{ + TEST(int, 4, 4); + +#if defined(__LP64__) + TEST(long, 8, 8); + TEST(void *, 8, 8); + TEST(long long, 8, 8); +#elif defined(__LLP64__) + TEST(long, 4, 4); + TEST(void *, 8, 8); + TEST(long long, 8, 8); +#elif defined(__x86_64__) + TEST(long, 4, 4); + TEST(void *, 4, 4); + TEST(long long, 8, 8); +#else + TEST(long, 4, 4); + TEST(void *, 4, 4); + TEST(long long, 8, 4); +#endif + + return 0; +} + +/* + * check-name: abi-integer + */ diff --git a/usr/src/tools/smatch/src/validation/address_space.c b/usr/src/tools/smatch/src/validation/address_space.c index c55b78df0f..a0c8bf5d18 100644 --- a/usr/src/tools/smatch/src/validation/address_space.c +++ b/usr/src/tools/smatch/src/validation/address_space.c @@ -12,6 +12,6 @@ static int sys_do_stuff(void __user *user_addr) * check-error-start address_space.c:7:28: warning: incorrect type in argument 1 (different address spaces) address_space.c:7:28: expected void *addr -address_space.c:7:28: got void <asn:1>*user_addr +address_space.c:7:28: got void <asn:1> *user_addr * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/array-implicit-size.c b/usr/src/tools/smatch/src/validation/array-implicit-size.c new file mode 100644 index 0000000000..7011008b63 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/array-implicit-size.c @@ -0,0 +1,26 @@ +static int array[] = { 0, 1, 2, 3, }; +_Static_assert(sizeof(array) == 4 * sizeof(int), "size of array"); + + +typedef int table_t[]; +static table_t tbl2 = { + 0, + 1, +}; +_Static_assert(sizeof(tbl2) == 2 * sizeof(int), "size of tbl2"); + +static table_t tbl1 = { + 0, +}; +_Static_assert(sizeof(tbl1) == 1 * sizeof(int), "size of tbl1"); + +static table_t tbl3 = { + 0, + 1, + 2, +}; +_Static_assert(sizeof(tbl3) == 3 * sizeof(int), "size of tbl3"); + +/* + * check-name: array-implicit-size + */ diff --git a/usr/src/tools/smatch/src/validation/as-name.c b/usr/src/tools/smatch/src/validation/as-name.c new file mode 100644 index 0000000000..4dd65798d6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/as-name.c @@ -0,0 +1,17 @@ +#define __user __attribute__((address_space(__user))) + +extern void fun(void *addr); + +static void foo(void __user *ptr) +{ + return fun(ptr); +} +/* + * check-name: as-name attribute + * + * check-error-start +as-name.c:7:20: warning: incorrect type in argument 1 (different address spaces) +as-name.c:7:20: expected void *addr +as-name.c:7:20: got void __user *ptr + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/asm-inline.c b/usr/src/tools/smatch/src/validation/asm-inline.c new file mode 100644 index 0000000000..186286b353 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/asm-inline.c @@ -0,0 +1,52 @@ +static void foo(void) +{ + asm(""); + asm volatile ("v"); + asm inline ("i"); + asm volatile inline ("vi"); + asm inline volatile ("iv"); + + asm goto ("g" :::: label); + asm volatile goto ("vg" :::: label); + asm inline goto ("ig" :::: label); + asm volatile inline goto ("vig" :::: label); + asm inline volatile goto ("ivg" :::: label); + + asm goto volatile ("gv" :::: label); + asm goto inline ("gi" :::: label); + asm goto volatile inline ("gvi" :::: label); + asm goto inline volatile ("giv" :::: label); + asm volatile goto inline ("vgi" :::: label); + asm inline goto volatile ("giv" :::: label); + + // warn on duplicates + asm volatile volatile ("vv"); + asm inline inline ("ii"); + asm goto goto ("gg" :::: label); + + asm inline volatile inline ("ivi"); + asm inline goto inline ("igi" :::: label); + asm goto inline goto ("gig" :::: label); + asm goto volatile goto ("gvg" :::: label); + asm volatile inline volatile ("viv"); + asm volatile goto volatile ("vgv" :::: label); + +label: + ; +} + +/* + * check-name: asm-inline + * + * check-error-start +asm-inline.c:23:22: warning: duplicated asm modifier +asm-inline.c:24:20: warning: duplicated asm modifier +asm-inline.c:25:18: warning: duplicated asm modifier +asm-inline.c:27:29: warning: duplicated asm modifier +asm-inline.c:28:25: warning: duplicated asm modifier +asm-inline.c:29:25: warning: duplicated asm modifier +asm-inline.c:30:27: warning: duplicated asm modifier +asm-inline.c:31:29: warning: duplicated asm modifier +asm-inline.c:32:27: warning: duplicated asm modifier + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/attr-context.c b/usr/src/tools/smatch/src/validation/attr-context.c new file mode 100644 index 0000000000..00e54c6685 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/attr-context.c @@ -0,0 +1,40 @@ +static void a(void) __attribute__((context)); // KO +static void b(void) __attribute__((context())); // KO +static void c(void) __attribute__((context 1)); // KO +static void d(void) __attribute__((context 1,2)); // KO +static void e(void) __attribute__((context (1))); // !!!! +static void f(void) __attribute__((context(0))); // !!!! +static void g(void) __attribute__((context(0,1,2,3))); // KO + +static void h(void) __attribute__((context (1,2))); // OK +static void i(void) __attribute__((context(0,1))); // OK +static void j(void) __attribute__((context(0,1,2))); // OK + +extern int u, v; +static void x(void) __attribute__((context(0,1,v))); +static void y(void) __attribute__((context(0,u,1))); +static void z(void) __attribute__((context(0,u))); + +/* + * check-name: attr-context + * + * check-error-start +attr-context.c:1:43: error: Expected ( after context attribute +attr-context.c:1:43: error: got ) +attr-context.c:2:44: error: Expected , after context 1st argument +attr-context.c:2:44: error: got ) +attr-context.c:3:44: error: Expected ( after context attribute +attr-context.c:3:44: error: got 1 +attr-context.c:4:44: error: Expected ( after context attribute +attr-context.c:4:44: error: got 1 +attr-context.c:5:46: error: Expected , after context 1st argument +attr-context.c:5:46: error: got ) +attr-context.c:6:45: error: Expected , after context 1st argument +attr-context.c:6:45: error: got ) +attr-context.c:7:49: error: Expected ) after context 3rd argument +attr-context.c:7:49: error: got , +attr-context.c:14:48: error: bad constant expression +attr-context.c:15:46: error: bad constant expression +attr-context.c:16:46: error: bad constant expression + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/backend/arithmetic-ops.c b/usr/src/tools/smatch/src/validation/backend/arithmetic-ops.c index 55996d9c30..fc4f0e5a64 100644 --- a/usr/src/tools/smatch/src/validation/backend/arithmetic-ops.c +++ b/usr/src/tools/smatch/src/validation/backend/arithmetic-ops.c @@ -88,6 +88,26 @@ static unsigned int umod(unsigned int x, unsigned int y) return x % y; } +static int neg(int x) +{ + return -x; +} + +static unsigned int uneg(unsigned int x) +{ + return -x; +} + +static float fneg(float x) +{ + return -x; +} + +static double dneg(double x) +{ + return -x; +} + /* * check-name: Arithmetic operator code generation * check-command: sparsec -c $file -o tmp.o diff --git a/usr/src/tools/smatch/src/validation/backend/call-variadic.c b/usr/src/tools/smatch/src/validation/backend/call-variadic.c new file mode 100644 index 0000000000..4924e3f141 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/call-variadic.c @@ -0,0 +1,27 @@ +#define NULL ((void*)0) + +extern int print(const char *msg, ...); + +int foo(const char *fmt, int a, long l, int *p); +int foo(const char *fmt, int a, long l, int *p) +{ + return print(fmt, 'x', a, __LINE__, l, 0L, p, NULL); +} + +/* + * check-name: call-variadic + * check-command: sparse-llvm-dis -m64 $file + * + * check-output-start +; ModuleID = '<stdin>' +source_filename = "sparse" + +define i32 @foo(i8* %ARG1., i32 %ARG2., i64 %ARG3., i32* %ARG4.) { +L0: + %R5. = call i32 (i8*, ...) @print(i8* %ARG1., i32 120, i32 %ARG2., i32 8, i64 %ARG3., i64 0, i32* %ARG4., i8* null) + ret i32 %R5. +} + +declare i32 @print(i8*, ...) + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/backend/cast.c b/usr/src/tools/smatch/src/validation/backend/cast.c index 4ca531b53a..f41224093d 100644 --- a/usr/src/tools/smatch/src/validation/backend/cast.c +++ b/usr/src/tools/smatch/src/validation/backend/cast.c @@ -1,4 +1,5 @@ typedef _Bool bool; +typedef signed char schar; typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; @@ -14,6 +15,7 @@ typedef unsigned long long ulonglong; #define DEFINE_CASTS(from) \ DEFINE_CAST(from, bool) \ DEFINE_CAST(from, char) \ + DEFINE_CAST(from, schar) \ DEFINE_CAST(from, uchar) \ DEFINE_CAST(from, short) \ DEFINE_CAST(from, ushort) \ @@ -23,13 +25,12 @@ typedef unsigned long long ulonglong; DEFINE_CAST(from, ulong) \ DEFINE_CAST(from, longlong) \ DEFINE_CAST(from, ulonglong) \ -/* DEFINE_CAST(from, float) \ DEFINE_CAST(from, double) -*/ DEFINE_CASTS(bool) DEFINE_CASTS(char) +DEFINE_CASTS(schar) DEFINE_CASTS(uchar) DEFINE_CASTS(short) DEFINE_CASTS(ushort) @@ -39,10 +40,8 @@ DEFINE_CASTS(long) DEFINE_CASTS(ulong) DEFINE_CASTS(longlong) DEFINE_CASTS(ulonglong) -/* DEFINE_CASTS(float) DEFINE_CASTS(double) -*/ /* * check-name: Cast code generation diff --git a/usr/src/tools/smatch/src/validation/backend/compare-with-null.c b/usr/src/tools/smatch/src/validation/backend/compare-with-null.c new file mode 100644 index 0000000000..e23562bc56 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/compare-with-null.c @@ -0,0 +1,12 @@ +int tstv(void *p) { return !p; } +int cmpv(void *p) { return p == ((void*)0); } + +int tsti(int *p) { return !p; } +int cmpi(int *p) { return p == ((int *)0); } +int cmpx(int *p) { return p == ((void*)0); } + +/* + * check-name: compare-with-null + * check-command: sparsec -Wno-decl -c $file -o tmp.o + * check-output-ignore + */ diff --git a/usr/src/tools/smatch/src/validation/backend/constant-pointer.c b/usr/src/tools/smatch/src/validation/backend/constant-pointer.c new file mode 100644 index 0000000000..9012c7843e --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/constant-pointer.c @@ -0,0 +1,24 @@ +extern int *ip[]; + +void foo(void); +void foo(void) +{ + ip[0] = (void *)0L; + ip[1] = (int *)0L; + ip[2] = (void *)0; + ip[3] = (int *)0; + ip[4] = (void *)(long)0; + ip[5] = (int *)(long)0; + ip[6] = (void *)123; + ip[7] = (int *)123; + ip[8] = (void *)123L; + ip[9] = (int *)123L; + ip[10] = (void *)(long)123; + ip[11] = (int *)(long)123; +} + +/* + * check-name: constant pointers + * check-command: sparse-llvm $file + * check-output-ignore + */ diff --git a/usr/src/tools/smatch/src/validation/backend/degenerate-ptr.c b/usr/src/tools/smatch/src/validation/backend/degenerate-ptr.c new file mode 100644 index 0000000000..052eefd966 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/degenerate-ptr.c @@ -0,0 +1,72 @@ +extern int array[3]; +extern int matrix[3][3]; +extern int fun(int); + +extern int fia(int []); +extern int fip(int *); +extern int fim(int (*)[3]); +extern int fvp(void *); +extern int ffp(int (*)(int)); + +void call(void); +void call(void) +{ + fia(array); + + fip(array); + fim(matrix); + + fvp(array); + fvp(matrix); + + fvp(fun); + fvp(&fun); + ffp(fun); + ffp(&fun); +} + +void local(void); +void local(void) +{ + int *ip; + int (*im)[3]; + void *vp; + int (*fp)(int); + + ip = array; + im = matrix; + + vp = array; + vp = matrix; + + vp = fun; + vp = &fun; + fp = fun; + fp = &fun; +} + + +extern int *ip; +extern int (*im)[3]; +extern void *vp; +extern int (*fp)(int); + +void global(void); +void global(void) +{ + ip = array; + im = matrix; + + vp = array; + vp = matrix; + + vp = fun; + vp = &fun; + fp = fun; + fp = &fun; +} + +/* + * check-name: degenerated pointer handling + * check-command: sparsec -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/fn-ref.c b/usr/src/tools/smatch/src/validation/backend/fn-ref.c new file mode 100644 index 0000000000..d987a28b8e --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/fn-ref.c @@ -0,0 +1,32 @@ +extern int fun0(int a); +extern int fun1(int a); + +int foo(int a); +int foo(int a) +{ + int v = fun0(a); + return v; +} + +void *bar(int a) +{ + return fun1; +} + +int fun0(int a) +{ + return a + 0; +} + +int fun1(int a) +{ + return a + 1; +} + +/* + * check-name: llvm function reference + * check-command: sparse-llvm-dis -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: fun[0-9]\.[1-9] + */ diff --git a/usr/src/tools/smatch/src/validation/backend/function-ptr-xtype.c b/usr/src/tools/smatch/src/validation/backend/function-ptr-xtype.c new file mode 100644 index 0000000000..126fec8fa6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/function-ptr-xtype.c @@ -0,0 +1,37 @@ +typedef int (*binop_t)(int, int); +typedef int (*unop_t)(int); +typedef int (*idef_t)(void); +typedef long (*ldef_t)(void); +typedef void (*use_t)(int); + +// We want to 'fn' have several different types. +// The goal is for the ->priv member to be used +// with a type different from what it was first stored. + +int foo(void *fn, int arg1, int arg2); +int foo(void *fn, int arg1, int arg2) +{ + int res = 0; + + res += ((binop_t)fn)(arg1, arg2); + res += ((unop_t)fn)(arg1); + res += ((ldef_t)fn)(); + res += ((idef_t)fn)(); + ((use_t)fn)(res); + return res; +} + +int bar(int (*fn)(int), int arg1, int arg2); +int bar(int (*fn)(int), int arg1, int arg2) +{ + int res = 0; + + res += ((binop_t)fn)(arg1, arg2); + res += fn(arg1); + return res; +} + +/* + * check-name: mutate function pointer's type + * check-command: sparsec -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/function-ptr.c b/usr/src/tools/smatch/src/validation/backend/function-ptr.c index 35fb678af0..4fac9dc210 100644 --- a/usr/src/tools/smatch/src/validation/backend/function-ptr.c +++ b/usr/src/tools/smatch/src/validation/backend/function-ptr.c @@ -1,8 +1,150 @@ -typedef int (*fn_t)(int x, int y); +extern int ival; +extern int *ipval; +extern int array[3]; +extern int matrix[3][3]; +extern int fun(int); -static int run(fn_t fn, int x, int y) +// via an argument +void arg(int a, int *p, int (*fb)(unsigned char), int (*fi)(int), int (*fl)(long), int (*fv)(void), int (*fip)(int *), int (*fim)(int (*)[3]), int (*fvp)(void *), int (*ffp)(int (*)(int))); +void arg(int a, int *p, int (*fb)(unsigned char), int (*fi)(int), int (*fl)(long), int (*fv)(void), int (*fip)(int *), int (*fim)(int (*)[3]), int (*fvp)(void *), int (*ffp)(int (*)(int))) { - return fn(x, y); + fv(); + + fb(a); + fi(a); + fl(a); + fb(123); + fi(123); + fl(123); + fb(123L); + fi(123L); + fl(123L); + fb(ival); + fi(ival); + fl(ival); + + fip(p); + fip((void*)0); + fip(ipval); + fip(&ival); + fip(array); + fim(matrix); + + fvp(p); + fvp((void*)0); + fvp(ipval); + fvp(&ival); + fvp(array); + fvp(matrix); + + fvp(fun); + fvp(&fun); + ffp(fun); + ffp(&fun); +} + +// a global +extern int (*fb)(unsigned char); +extern int (*fi)(int); +extern int (*fl)(long); +extern int (*fv)(void); +extern int (*fip)(int *); +extern int (*fim)(int (*)[3]); +extern int (*fvp)(void *); +extern int (*ffp)(int (*)(int)); + +void glb(int a, int *p); +void glb(int a, int *p) +{ + fv(); + + fb(a); + fi(a); + fl(a); + fb(123); + fi(123); + fl(123); + fb(123L); + fi(123L); + fl(123L); + fb(ival); + fi(ival); + fl(ival); + + fip(p); + fip((void*)0); + fip(ipval); + fip(&ival); + fip(array); + fim(matrix); + + fvp(p); + fvp((void*)0); + fvp(ipval); + fvp(&ival); + fvp(array); + fvp(matrix); + + fvp(fun); + fvp(&fun); + ffp(fun); + ffp(&fun); +} + +// via a struct member: +// -> force to create a register containing the function pointer +struct ops { + int (*fb)(unsigned char); + int (*fi)(int); + int (*fl)(long); + int (*fv)(void); + int (*fip)(int *); + int (*fim)(int (*)[3]); + int (*fvp)(void *); + int (*ffp)(int (*)(int)); + + int (*const cfi)(int); // for the fun of it +}; + +void ops(int a, int *p, struct ops *ops); +void ops(int a, int *p, struct ops *ops) +{ + ops->fv(); + + ops->fb(a); + ops->fi(a); + ops->fl(a); + ops->fb(123); + ops->fi(123); + ops->fl(123); + ops->fb(123L); + ops->fi(123L); + ops->fl(123L); + ops->fb(ival); + ops->fi(ival); + ops->fl(ival); + + ops->fip(p); + ops->fip((void*)0); + ops->fip(ipval); + ops->fip(&ival); + ops->fip(array); + ops->fim(matrix); + + ops->fvp(p); + ops->fvp((void*)0); + ops->fvp(ipval); + ops->fvp(&ival); + ops->fvp(array); + ops->fvp(matrix); + + ops->fvp(fun); + ops->fvp(&fun); + ops->ffp(fun); + ops->ffp(&fun); + ops->fvp(fi); + + ops->cfi(42); } /* diff --git a/usr/src/tools/smatch/src/validation/backend/label-as-value.c b/usr/src/tools/smatch/src/validation/backend/label-as-value.c new file mode 100644 index 0000000000..ec436f3902 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/label-as-value.c @@ -0,0 +1,13 @@ +void *foo(void *def); +void *foo(void *def) +{ + if (!def) +yes: return &&yes; + + return def; +} + +/* + * check-name: label-as-value + * check-command: sparsec -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/load-global.c b/usr/src/tools/smatch/src/validation/backend/load-global.c new file mode 100644 index 0000000000..16ad03bdb3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/load-global.c @@ -0,0 +1,21 @@ +const char *s = "abc"; +int x = 4; +int y; + +int *p = &x; +int *q; + +int loadn(void) { return y; } +int loadi(void) { return x; } + +const char *loads(void) { return s; } + +int *retpn(void) { return q; } +int loadpn(void) { return *q; } +int *retpi(void) { return p; } +int loadpi(void) { return *p; } + +/* + * check-name: use simple value from global vars + * check-command: sparsec -Wno-decl -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/pointer-add.c b/usr/src/tools/smatch/src/validation/backend/pointer-add.c new file mode 100644 index 0000000000..d4615140c4 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/pointer-add.c @@ -0,0 +1,54 @@ +char *caddv(char *p, int o) { char *r = p; r = r + o; return r; } +void *vaddv(void *p, int o) { void *r = p; r = r + o; return r; } +int *iaddv(int *p, int o) { int *r = p; r = r + o; return r; } + +char *caddc(char *p, int o) { char *r = p; r = r + 3; return r; } +void *vaddc(void *p, int o) { void *r = p; r = r + 3; return r; } +int *iaddc(int *p, int o) { int *r = p; r = r + 3; return r; } + +char *cincv(char *p, int o) { char *r = p; r += o; return r; } +void *vincv(void *p, int o) { void *r = p; r += o; return r; } +int *iincv(int *p, int o) { int *r = p; r += o; return r; } + +char *cincc(char *p, int o) { char *r = p; r += 3; return r; } +void *vincc(void *p, int o) { void *r = p; r += 3; return r; } +int *iincc(int *p, int o) { int *r = p; r += 3; return r; } + + +char *ciniaddv(char *p, int o) { char *r = p + o; return r; } +void *viniaddv(void *p, int o) { void *r = p + o; return r; } +int *iiniaddv(int *p, int o) { int *r = p + o; return r; } + +char *ciniaddc(char *p, int o) { char *r = p + 3; return r; } +void *viniaddc(void *p, int o) { void *r = p + 3; return r; } +int *iiniaddc(int *p, int o) { int *r = p + 3; return r; } + +char *ciniincv(char *p, int o) { char *r = p += o; return r; } +void *viniincv(void *p, int o) { void *r = p += o; return r; } +int *iiniincv(int *p, int o) { int *r = p += o; return r; } + +char *ciniincc(char *p, int o) { char *r = p += 3; return r; } +void *viniincc(void *p, int o) { void *r = p += 3; return r; } +int *iiniincc(int *p, int o) { int *r = p += 3; return r; } + + +char *cretaddv(char *p, int o) { return p + o; } +void *vretaddv(void *p, int o) { return p + o; } +int *iretaddv(int *p, int o) { return p + o; } + +char *cretaddc(char *p, int o) { return p + 3; } +void *vretaddc(void *p, int o) { return p + 3; } +int *iretaddc(int *p, int o) { return p + 3; } + +char *cretincv(char *p, int o) { return p += o; } +void *vretincv(void *p, int o) { return p += o; } +int *iretincv(int *p, int o) { return p += o; } + +char *cretincc(char *p, int o) { return p += 3; } +void *vretincc(void *p, int o) { return p += 3; } +int *iretincc(int *p, int o) { return p += 3; } + +/* + * check-name: pointer-add + * check-command: sparsec -Wno-decl -c $file -o r.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/pointer-cmp.c b/usr/src/tools/smatch/src/validation/backend/pointer-cmp.c new file mode 100644 index 0000000000..7e0d8d7dac --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/pointer-cmp.c @@ -0,0 +1,12 @@ +int cmpint( int x, int y) { return x == y; } +int cmpflt( float x, float y) { return x == y; } +int cmpvptr(void *x, void *y) { return x == y; } +int cmpiptr(int *x, int *y) { return x == y; } + +int cmpmptr(long x, int *y) { return (int*)x == y; } +int cmpnptr(int *x, long y) { return x == (int*)y; } + +/* + * check-name: pointer comparison + * check-command: sparsec -Wno-decl -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/pointer-param.c b/usr/src/tools/smatch/src/validation/backend/pointer-param.c new file mode 100644 index 0000000000..65c23a5bb0 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/pointer-param.c @@ -0,0 +1,42 @@ +extern int gfun(int); +static int sfun(int a) { return a; } + +void usei(int *); +void usef(int (*)(int)); +void usev(void *); + +void foo(int *p, int a[5], int (*pfun)(int)); +void foo(int *p, int a[5], int (*pfun)(int)) +{ + extern int valg[5], valh[5], vali[5]; + static int vals[5], valt[5], valr[5]; + int vala[5], valb[5], valc[5]; + + usei(p); + usei(valg); + usei(&valh[0]); + usei(&vali[1]); + usei(vals); + usei(&valt[0]); + usei(&valr[1]); + usei(vala); + usei(&valb[0]); + usei(&valc[1]); + + usef(pfun); + usef(gfun); + usef(&gfun); + usef(sfun); + usef(&sfun); + + usev(pfun); + usev(gfun); + usev(&gfun); + usev(sfun); + usev(&sfun); +} + +/* + * check-name: pointer-param + * check-command: sparsec -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/pointer-sub.c b/usr/src/tools/smatch/src/validation/backend/pointer-sub.c new file mode 100644 index 0000000000..5c99f4faf0 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/pointer-sub.c @@ -0,0 +1,17 @@ +long subv0(void *p, int a) { return p - ((void*)0); } +long subvc(void *p, int a) { return p - ((void*)8); } +long subva(void *p, int a) { return p - ((void*)a); } +long subvq(void *p, void *q) { return p - q; } + +long subi0(int *p, int a) { return p - ((int *)0); } +long subic(int *p, int a) { return p - ((int *)8); } +long subia(int *p, int a) { return p - ((int *)a); } +long subiq(int *p, int *q) { return p - q; } + +long subvm3(void *p, int a) { return (p - ((void*)0)) * 3; } +long subvx3(void *p, int a) { return (p - ((void*)0)) ^ 3; } + +/* + * check-name: pointer-sub + * check-command: sparsec -Wno-int-to-pointer-cast -Wno-decl -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/setval.c b/usr/src/tools/smatch/src/validation/backend/setval.c new file mode 100644 index 0000000000..66ac970a9b --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/setval.c @@ -0,0 +1,7 @@ +double setfval64(void) { return 1.23; } +float setfval32(void) { return 1.23F; } + +/* + * check-name: setval-float + * check-command: sparsec -Wno-decl -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/shift-special.c b/usr/src/tools/smatch/src/validation/backend/shift-special.c new file mode 100644 index 0000000000..9b68237195 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/shift-special.c @@ -0,0 +1,13 @@ +long shift(long a, short b); +long shift(long a, short b) +{ + long r1 = a << b; + long r2 = b << a; + + return r1 + r2; +} + +/* + * check-name: shift-special + * check-command: sparsec -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/store-x2.c b/usr/src/tools/smatch/src/validation/backend/store-x2.c new file mode 100644 index 0000000000..5ccc9b43ac --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/store-x2.c @@ -0,0 +1,16 @@ +void foo(int *p, int a, int b); +void foo(int *p, int a, int b) +{ + int c = a + b; + + p[0] = c; + p[1] = c; +} + +/* + * check-name: store-x2 + * check-command: sparsec -c $file -o tmp.o + * check-description: Verify in output_op_store() that + * the first store doesn't mess anymore with the + * 'target' and thus making the second store unusable. + */ diff --git a/usr/src/tools/smatch/src/validation/backend/string-value.c b/usr/src/tools/smatch/src/validation/backend/string-value.c new file mode 100644 index 0000000000..ae0b7b745a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/string-value.c @@ -0,0 +1,21 @@ +extern void use(const char *); + +const char *ret(void) +{ + return "abc"; +} + +const char *add(void) +{ + return "def" + 1; +} + +void call(void) +{ + use("ijk"); +} + +/* + * check-name: string-value + * check-command: sparsec -Wno-decl -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/sum.c b/usr/src/tools/smatch/src/validation/backend/sum.c index 06042999f4..fa51120e1c 100644 --- a/usr/src/tools/smatch/src/validation/backend/sum.c +++ b/usr/src/tools/smatch/src/validation/backend/sum.c @@ -19,7 +19,7 @@ int main(int argc, char **argv) /* * check-name: sum from 1 to n - * check-command: sparsei $file + * check-command: sparsei --no-jit $file * * check-output-start 15 diff --git a/usr/src/tools/smatch/src/validation/backend/switch.c b/usr/src/tools/smatch/src/validation/backend/switch.c new file mode 100644 index 0000000000..499b5e3cd3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/switch.c @@ -0,0 +1,248 @@ +int def(void); +int r0(void); +int r1(void); +int r2(void); +int r3(void); +int r4(void); +int r5(void); +int r6(void); +int r7(void); +int r8(void); +int r9(void); + +int small(int a) +{ + switch (a) { + case 0: return r0(); + case 1: return r1(); + case 2: return r2(); + } + + return def(); +} + +int densefull(int a) +{ + switch (a) { + case 0: return r0(); + case 1: return r1(); + case 2: return r2(); + case 3: return r3(); + case 4: return r4(); + case 5: return r5(); + case 6: return r6(); + case 7: return r7(); + case 8: return r8(); + case 9: return r9(); + } + + return def(); +} + +int densepart(int a) +{ + switch (a) { + case 0: return r0(); + case 1: return r1(); + case 2: return r2(); + case 3: return r3(); + case 4: return r4(); + + case 6: return r6(); + case 7: return r7(); + case 8: return r8(); + case 9: return r9(); + } + + return def(); +} + +int dense_dense_20(int a) +{ + switch (a) { + case 0: return r0(); + case 1: return r1(); + case 2: return r2(); + case 3: return r3(); + case 4: return r4(); + case 5: return r5(); + case 6: return r6(); + case 7: return r7(); + case 8: return r8(); + case 9: return r9(); + + case 20: return r0(); + case 21: return r1(); + case 22: return r2(); + case 23: return r3(); + case 24: return r4(); + case 25: return r5(); + case 26: return r6(); + case 27: return r7(); + case 28: return r8(); + case 29: return r9(); + } + + return def(); +} + +int dense_dense_100(int a) +{ + switch (a) { + case 0: return r0(); + case 1: return r1(); + case 2: return r2(); + case 3: return r3(); + case 4: return r4(); + case 5: return r5(); + case 6: return r6(); + case 7: return r7(); + case 8: return r8(); + case 9: return r9(); + + case 100: return r0(); + case 101: return r1(); + case 102: return r2(); + case 103: return r3(); + case 104: return r4(); + case 105: return r5(); + case 106: return r6(); + case 107: return r7(); + case 108: return r8(); + case 109: return r9(); + } + + return def(); +} + +int dense_dense_1000(int a) +{ + switch (a) { + case 0: return r0(); + case 1: return r1(); + case 2: return r2(); + case 3: return r3(); + case 4: return r4(); + case 5: return r5(); + case 6: return r6(); + case 7: return r7(); + case 8: return r8(); + case 9: return r9(); + + case 1000: return r0(); + case 1001: return r1(); + case 1002: return r2(); + case 1003: return r3(); + case 1004: return r4(); + case 1005: return r5(); + case 1006: return r6(); + case 1007: return r7(); + case 1008: return r8(); + case 1009: return r9(); + } + + return def(); +} + +int sparse(int a) +{ + switch (a) { + case 0: return r0(); + case 3: return r1(); + case 12: return r2(); + case 31: return r3(); + case 54: return r4(); + case 75: return r5(); + case 96: return r6(); + case 107: return r7(); + case 189: return r8(); + case 999: return r9(); + } + + return def(); +} + +int range_simple(int a) +{ + switch (a) { + case 1 ... 9: return r0(); + } + + return def(); +} + +int range_complex(int a) +{ + switch (a) { + case -1: return r0(); + case 1 ... 9: return r0(); + case 10 ... 19: return r1(); + case 200 ... 202: return r2(); + case 300 ... 303: return r3(); + } + + return def(); +} + +void switch_call(int a) +{ + int r; + + switch (a) { + case 0: r0(); break; + case 1: r1(); break; + case 2: r2(); break; + case 3: r3(); break; + case 4: r4(); break; + case 5: r5(); break; + case 6: r6(); break; + case 7: r7(); break; + case 8: r8(); break; + case 9: r9(); break; + } +} + +int switch_retcall(int a) +{ + int r = 0; + + switch (a) { + case 0: r = r0(); break; + case 1: r = r1(); break; + case 2: r = r2(); break; + case 3: r = r3(); break; + case 4: r = r4(); break; + case 5: r = r5(); break; + case 6: r = r6(); break; + case 7: r = r7(); break; + case 8: r = r8(); break; + case 9: r = r9(); break; + } + + return r; +} + +int switch_cmov(int a) +{ + int r; + + switch (a) { + case 0: r = 3; break; + case 1: r = 1; break; + case 2: r = 7; break; + case 3: r = 2; break; + case 4: r = 9; break; + + case 6: r = 5; break; + case 7: r = 8; break; + case 8: r = 6; break; + case 9: r = 4; break; + } + + return r; +} + +/* + * check-name: llvm-switch + * check-command: sparsec -Wno-decl -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/symaddr.c b/usr/src/tools/smatch/src/validation/backend/symaddr.c new file mode 100644 index 0000000000..2943b8cb41 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/symaddr.c @@ -0,0 +1,70 @@ +extern void useip(int *); +extern void useia(int (*)[3]); +extern void usevp(void *); +static int sfun(void) { return 0; } +static int spun(void) { return 0; } + +void lfoo(int *p, int a) +{ + int larra[3], larrb[3], larrc[3], larrd[3], larre[3], larrf[3]; + useip(p); + useip(larra); + useip(larrb + 1); + useip(larrc + a); + useip(&larrd[1]); + useip(&larre[a]); + useia(&larrf); +} + +static int sarra[3], sarrb[3], sarrc[3], sarrd[3], sarre[3], sarrf[3]; +static int s, sfun(void), spun(void); +void sfoo(int *p, int a) +{ + useip(p); + useip(&s); + useip(sarra); + useip(sarrb + 1); + useip(sarrc + a); + useip(&sarrd[1]); + useip(&sarre[a]); + useia(&sarrf); + usevp(sfun); + usevp(&spun); +} + +extern int xarra[3], xarrb[3], xarrc[3], xarrd[3], xarre[3], xarrf[3]; +extern int x, xfun(void), xpun(void); +void xfoo(int *p, int a) +{ + useip(p); + useip(&x); + useip(xarra); + useip(xarrb + 1); + useip(xarrc + a); + useip(&xarrd[1]); + useip(&xarre[a]); + useia(&xarrf); + usevp(xfun); + usevp(&xpun); +} + +int garra[3], garrb[3], garrc[3], garrd[3], garre[3], garrf[3]; +int g, gfun(void), gpun(void); +void gfoo(int *p, int a) +{ + useip(p); + useip(&g); + useip(garra); + useip(garrb + 1); + useip(garrc + a); + useip(&garrd[1]); + useip(&garre[a]); + useia(&garrf); + usevp(gfun); + usevp(&gpun); +} + +/* + * check-name: symbol address + * check-command: sparsec -Wno-decl -c $file -o tmp.o + */ diff --git a/usr/src/tools/smatch/src/validation/backend/type-constant.c b/usr/src/tools/smatch/src/validation/backend/type-constant.c new file mode 100644 index 0000000000..596a6819dd --- /dev/null +++ b/usr/src/tools/smatch/src/validation/backend/type-constant.c @@ -0,0 +1,23 @@ +char creti(void) { return 3; } +int ireti(void) { return 3; } +long lreti(void) { return 3; } + +char cinii(void) { char r = 3; return r; } +int iinii(void) { int r = 3; return r; } +long linii(void) { long r = 3; return r; } + + +void *vretn(void) { return (void*)0; } +char *cretn(void) { return (void*)0; } +int *iretn(void) { return (void*)0; } +long *lretn(void) { return (void*)0; } + +void *vinin(void) { void *r = (void*)0; return r; } +char *cinin(void) { char *r = (void*)0; return r; } +int *iinin(void) { int *r = (void*)0; return r; } +long *linin(void) { long *r = (void*)0; return r; } + +/* + * check-name: type-constant + * check-command: sparsec -Wno-decl -c $file -o r.o + */ diff --git a/usr/src/tools/smatch/src/validation/bad-return-type.c b/usr/src/tools/smatch/src/validation/bad-return-type.c new file mode 100644 index 0000000000..0f3b3f516d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bad-return-type.c @@ -0,0 +1,19 @@ +void foo(int a) +{ + return a; +} + +int bar(void) +{ + return; +} + +/* + * check-name: bad return type + * check-command: sparse -Wno-decl $file + * + * check-error-start +bad-return-type.c:3:16: error: return expression in void function +bad-return-type.c:8:9: error: return with no return value + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/bad-type-twice0.c b/usr/src/tools/smatch/src/validation/bad-type-twice0.c new file mode 100644 index 0000000000..5d107a625f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bad-type-twice0.c @@ -0,0 +1,13 @@ +static int foo(a) +{ + return a ? : 1; +} + +/* + * check-name: bad-type-twice0 + * + * check-error-start +bad-type-twice0.c:3:16: error: incorrect type in conditional (non-scalar type) +bad-type-twice0.c:3:16: got incomplete type a + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/bad-type-twice1.c b/usr/src/tools/smatch/src/validation/bad-type-twice1.c new file mode 100644 index 0000000000..cc81662afa --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bad-type-twice1.c @@ -0,0 +1,16 @@ +static unsigned long foo(unsigned long val, void *ref) +{ + if (val >= ref) + val = 0; + return val; +} + +/* + * check-name: bad-type-twice1 + * + * check-error-start +bad-type-twice1.c:3:17: error: incompatible types for operation (>=) +bad-type-twice1.c:3:17: left side has type unsigned long val +bad-type-twice1.c:3:17: right side has type void *ref + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/bad-type-twice2.c b/usr/src/tools/smatch/src/validation/bad-type-twice2.c new file mode 100644 index 0000000000..0aadd7a30d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bad-type-twice2.c @@ -0,0 +1,18 @@ +extern type_t fun(int); + +int foo(int x, int y) +{ + return ((int)fun(y)) + x; +} + +/* + * check-name: bad-type-twice2 + * + * check-error-start +bad-type-twice2.c:1:8: warning: 'type_t' has implicit type +bad-type-twice2.c:1:15: error: Expected ; at end of declaration +bad-type-twice2.c:1:15: error: got fun +bad-type-twice2.c:5:22: error: undefined identifier 'fun' +bad-type-twice2.c:5:18: error: cast from unknown type + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/bitfield-bool-layout.c b/usr/src/tools/smatch/src/validation/bitfield-bool-layout.c new file mode 100644 index 0000000000..4e0a2b4a2b --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bitfield-bool-layout.c @@ -0,0 +1,26 @@ +struct bfb { + _Bool a:1; + _Bool f:1; + _Bool z:1; +}; + + +struct bfb foo(struct bfb s) +{ + return s; +} + +/* + * check-name: bitfield-bool-layout + * check-description: given that bool is here 1-bit wide + * each field here above completely 'fill' a bool. + * Thus 3 bools need to be allocated, but since the + * alignment is 1-byte the result has a size of 3 + * bytes, 24 bits thus instead of 8. + * check-command: test-linearize -Wno-decl $file + * + * check-known-to-fail + * check-output-ignore + * check-output-excludes: ret\\.24 + * check-output-contains: ret\\.8 + */ diff --git a/usr/src/tools/smatch/src/validation/bitfield-kr.c b/usr/src/tools/smatch/src/validation/bitfield-kr.c new file mode 100644 index 0000000000..ba66c5cd71 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bitfield-kr.c @@ -0,0 +1,14 @@ +static int foo(b) + int b: 4; +{ + return 0; +} + +/* + * check-name: bitfield in K&R + * + * check-known-to-fail + * check-error-start +bitfield-kr.c:2:9: error: bitfield in K&R declaration of 'foo' + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/bitwise-cast-ptr.c b/usr/src/tools/smatch/src/validation/bitwise-cast-ptr.c new file mode 100644 index 0000000000..77927f49b5 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bitwise-cast-ptr.c @@ -0,0 +1,33 @@ +#define __bitwise __attribute__((bitwise)) +#define __force __attribute__((force)) + +typedef unsigned int u32; +typedef unsigned int __bitwise __be32; + +static __be32* tobi(u32 *x) +{ + return x; // should warn, implicit cast +} + +static __be32* tobe(u32 *x) +{ + return (__be32 *) x; // should warn, explicit cast +} + +static __be32* tobf(u32 *x) +{ + return (__force __be32 *) x; // should not warn, forced cast + return (__be32 __force *) x; // should not warn, forced cast +} + +/* + * check-name: cast of bitwise pointers + * check-command: sparse -Wbitwise -Wbitwise-pointer $file + * + * check-error-start +bitwise-cast-ptr.c:9:16: warning: incorrect type in return expression (different base types) +bitwise-cast-ptr.c:9:16: expected restricted __be32 [usertype] * +bitwise-cast-ptr.c:9:16: got unsigned int [usertype] *x +bitwise-cast-ptr.c:14:17: warning: cast to restricted __be32 [usertype] * + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/bitwise-cast.c b/usr/src/tools/smatch/src/validation/bitwise-cast.c index baeca29e75..0583461cb7 100644 --- a/usr/src/tools/smatch/src/validation/bitwise-cast.c +++ b/usr/src/tools/smatch/src/validation/bitwise-cast.c @@ -29,6 +29,12 @@ static __be32 quux(void) return (__be32)1729; } +/* Explicit case of nonzero forced, legal */ +static __be32 quuy(void) +{ + return (__attribute__((force)) __be32) 1730; +} + /* * check-name: conversions to bitwise types * check-command: sparse -Wbitwise $file diff --git a/usr/src/tools/smatch/src/validation/bool-cast-explicit.c b/usr/src/tools/smatch/src/validation/bool-cast-explicit.c deleted file mode 100644 index dbb67cc420..0000000000 --- a/usr/src/tools/smatch/src/validation/bool-cast-explicit.c +++ /dev/null @@ -1,22 +0,0 @@ -typedef unsigned int u32; -typedef int s32; -typedef void *vdp; -typedef int *sip; -typedef double dbl; -typedef unsigned short __attribute__((bitwise)) le16; - -static _Bool fs32(s32 a) { return (_Bool)a; } -static _Bool fu32(u32 a) { return (_Bool)a; } -static _Bool fvdp(vdp a) { return (_Bool)a; } -static _Bool fsip(sip a) { return (_Bool)a; } -static _Bool fdbl(dbl a) { return (_Bool)a; } -static _Bool ffun(void) { return (_Bool)ffun; } - -static _Bool fres(le16 a) { return (_Bool)a; } - -/* - * check-name: bool-cast-explicit - * check-command: test-linearize -m64 $file - * check-output-ignore - * check-output-excludes: cast\\. - */ diff --git a/usr/src/tools/smatch/src/validation/bool-cast-implicit.c b/usr/src/tools/smatch/src/validation/bool-cast-implicit.c deleted file mode 100644 index 9d89443b10..0000000000 --- a/usr/src/tools/smatch/src/validation/bool-cast-implicit.c +++ /dev/null @@ -1,25 +0,0 @@ -typedef unsigned int u32; -typedef int s32; -typedef void *vdp; -typedef int *sip; -typedef double dbl; -typedef unsigned short __attribute__((bitwise)) le16; - -static _Bool fs32(s32 a) { return a; } -static _Bool fu32(u32 a) { return a; } -static _Bool fvdp(vdp a) { return a; } -static _Bool fsip(sip a) { return a; } -static _Bool fdbl(dbl a) { return a; } -static _Bool ffun(void) { return ffun; } - -static _Bool fres(le16 a) { return a; } - -/* - * check-name: bool-cast-implicit - * check-command: test-linearize -m64 $file - * check-output-ignore - * check-output-excludes: cast\\. - * - * check-error-start - * check-error-end - */ diff --git a/usr/src/tools/smatch/src/validation/bool-float.c b/usr/src/tools/smatch/src/validation/bool-float.c new file mode 100644 index 0000000000..eadf4cf09a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bool-float.c @@ -0,0 +1,9 @@ +int ftst(double a) { return !a; } + +/* + * check-name: not-operator on float + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * + * check-output-excludes: \\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/bug-bad-type.c b/usr/src/tools/smatch/src/validation/bug-bad-type.c new file mode 100644 index 0000000000..0e00efefa1 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bug-bad-type.c @@ -0,0 +1,18 @@ +struct s { + int i; +}; + +long a; +void foo(void) +{ + (struct s) { .i = (foo - a), }; +} + +/* + * check-name: bug-bad-type + * + * check-error-start +bug-bad-type.c:5:6: warning: symbol 'a' was not declared. Should it be static? +bug-bad-type.c:8:32: error: arithmetics on pointers to functions + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/bug-crash16.c b/usr/src/tools/smatch/src/validation/bug-crash16.c new file mode 100644 index 0000000000..03a830496d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bug-crash16.c @@ -0,0 +1,11 @@ +static void foo(void) +{ + int b[] = { 8 }; + int c; + for (;;) + b[c] = b[0]; +} + +/* + * check-name: bug-crash16 + */ diff --git a/usr/src/tools/smatch/src/validation/bug-expand-union0.c b/usr/src/tools/smatch/src/validation/bug-expand-union0.c new file mode 100644 index 0000000000..dd6d60c3e2 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bug-expand-union0.c @@ -0,0 +1,21 @@ +union u { + char c; + float f; +}; + +static int foo(void) +{ + union u u = { .f = 0.123 }; + return u.c; +} + +/* + * check-name: bug-expand-union + * check description: must not infer the value from the float + * check-command: test-linearize $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: load\\. + * check-output-excludes: ret\\..*\\$ + */ diff --git a/usr/src/tools/smatch/src/validation/bug-expand-union1.c b/usr/src/tools/smatch/src/validation/bug-expand-union1.c new file mode 100644 index 0000000000..582a1f4f83 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bug-expand-union1.c @@ -0,0 +1,20 @@ +union u { + int i; + float f; +}; + +static int foo(void) +{ + union u u = { .f = 0.123 }; + return u.i; +} + +/* + * check-name: bug-expand-union + * check description: must not infer the value from the float + * check-command: test-linearize $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: load\\. + */ diff --git a/usr/src/tools/smatch/src/validation/bug-rshift-ub.c b/usr/src/tools/smatch/src/validation/bug-rshift-ub.c new file mode 100644 index 0000000000..7654abbd8f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/bug-rshift-ub.c @@ -0,0 +1,16 @@ +enum a { + A = ~0ULL, +}; + +static enum a a = A; + +/* + * check-name: bug-rshift-ub + * check-description: + * This test trigger(ed) a bug on x86 caused by a + * full width shift (which is UB), expecting to get + * 0 but giving the unshifted value and as result + * the type is invalid: + * warning: incorrect type in initializer (invalid types) + * expected bad type enum a static [toplevel] a + */ diff --git a/usr/src/tools/smatch/src/validation/builtin-arith.c b/usr/src/tools/smatch/src/validation/builtin-arith.c new file mode 100644 index 0000000000..d08c93dab4 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/builtin-arith.c @@ -0,0 +1,52 @@ + + +void test(void (*fun)(void)); +void test(void (*fun)(void)) +{ + typedef typeof(__builtin_trap) t; // OK + void (*f)(void); + int i; + + f = __builtin_trap; + f = &__builtin_trap; + f = *__builtin_trap; // OK for GCC + f = __builtin_trap + 0; + f = __builtin_trap + 1; + f = __builtin_trap - 1; + + // (void) __builtin_trap; + f = (void*) __builtin_trap; + f = (unsigned long) __builtin_trap; + + i = !__builtin_trap; + i = (__builtin_trap > fun); + i = (__builtin_trap == fun); + i = (fun < __builtin_trap); + i = (fun == __builtin_trap); + + __builtin_trap - fun; + fun - __builtin_trap; +} + +/* + * check-name: builtin arithmetic + * check-command: sparse -Wno-decl $file + * check-known-to-fail + * + * check-error-start +builtin-arith.c:10:xx: error: ... +builtin-arith.c:11:xx: error: ... +builtin-arith.c:13:xx: error: arithmetics on pointers to functions +builtin-arith.c:14:xx: error: arithmetics on pointers to functions +builtin-arith.c:15:xx: error: arithmetics on pointers to functions +builtin-arith.c:18:xx: error: ... +builtin-arith.c:19:xx: error: ... +builtin-arith.c:21:xx: error: ... +builtin-arith.c:22:xx: error: ... +builtin-arith.c:23:xx: error: ... +builtin-arith.c:24:xx: error: ... +builtin-arith.c:25:xx: error: ... +builtin-arith.c:27:24: error: subtraction of functions? Share your drugs +builtin-arith.c:28:13: error: subtraction of functions? Share your drugs + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/builtin-bswap-variable.c b/usr/src/tools/smatch/src/validation/builtin-bswap-variable.c index 738ba2a450..40ad641349 100644 --- a/usr/src/tools/smatch/src/validation/builtin-bswap-variable.c +++ b/usr/src/tools/smatch/src/validation/builtin-bswap-variable.c @@ -25,8 +25,8 @@ static u64 swap64v(u32 a) * * check-output-ignore * check-output-contains:call.16 .* __builtin_bswap16 - * check-output-contains:cast.32 .* (64) %arg1 + * check-output-contains:trunc.32 .* (64) %arg1 * check-output-contains:call.32 .* __builtin_bswap32 - * check-output-contains:cast.64 .* (32) %arg1 + * check-output-contains:zext.64 .* (32) %arg1 * check-output-contains:call.64 .* __builtin_bswap64 */ diff --git a/usr/src/tools/smatch/src/validation/builtin-fp-unop.c b/usr/src/tools/smatch/src/validation/builtin-fp-unop.c new file mode 100644 index 0000000000..f42c587b7d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/builtin-fp-unop.c @@ -0,0 +1,95 @@ +static void test_not_enough_args(void) +{ + __builtin_isfinite(); + __builtin_isinf(); + __builtin_isinf_sign(); + __builtin_isnan(); + __builtin_isnormal(); + __builtin_signbit(); +} + +static void test_too_many_args(double v) +{ + __builtin_isfinite(v, v); + __builtin_isinf(v, v); + __builtin_isinf_sign(v, v); + __builtin_isnan(v, v); + __builtin_isnormal(v, v); + __builtin_signbit(v, v); +} + +static void test_non_float(int v) +{ + __builtin_isfinite(v); + __builtin_isinf(v); + __builtin_isinf_sign(v); + __builtin_isnan(v); + __builtin_isnormal(v); + __builtin_signbit(v); +} + +static void test_float(float v) +{ + __builtin_isfinite(v); + __builtin_isinf(v); + __builtin_isinf_sign(v); + __builtin_isnan(v); + __builtin_isnormal(v); + __builtin_signbit(v); +} + +static void test_double(double v) +{ + __builtin_isfinite(v); + __builtin_isinf(v); + __builtin_isinf_sign(v); + __builtin_isnan(v); + __builtin_isnormal(v); + __builtin_signbit(v); +} + +static void test_ldouble(long double v) +{ + __builtin_isfinite(v); + __builtin_isinf(v); + __builtin_isinf_sign(v); + __builtin_isnan(v); + __builtin_isnormal(v); + __builtin_signbit(v); +} + +static void test_constant(void) +{ + __builtin_isfinite(0.0); + __builtin_isinf(0.0); + __builtin_isinf_sign(0.0); + __builtin_isnan(0.0); + __builtin_isnormal(0.0); + __builtin_signbit(0.0); +} + +/* + * check-name: builtin float-point unop + * check-command: sparse -Wno-decl $file + * + * check-error-start +builtin-fp-unop.c:3:27: error: not enough arguments for __builtin_isfinite +builtin-fp-unop.c:4:24: error: not enough arguments for __builtin_isinf +builtin-fp-unop.c:5:29: error: not enough arguments for __builtin_isinf_sign +builtin-fp-unop.c:6:24: error: not enough arguments for __builtin_isnan +builtin-fp-unop.c:7:27: error: not enough arguments for __builtin_isnormal +builtin-fp-unop.c:8:26: error: not enough arguments for __builtin_signbit +builtin-fp-unop.c:13:27: error: too many arguments for __builtin_isfinite +builtin-fp-unop.c:14:24: error: too many arguments for __builtin_isinf +builtin-fp-unop.c:15:29: error: too many arguments for __builtin_isinf_sign +builtin-fp-unop.c:16:24: error: too many arguments for __builtin_isnan +builtin-fp-unop.c:17:27: error: too many arguments for __builtin_isnormal +builtin-fp-unop.c:18:26: error: too many arguments for __builtin_signbit +builtin-fp-unop.c:23:27: error: non-floating-point argument in call to __builtin_isfinite() +builtin-fp-unop.c:24:24: error: non-floating-point argument in call to __builtin_isinf() +builtin-fp-unop.c:25:29: error: non-floating-point argument in call to __builtin_isinf_sign() +builtin-fp-unop.c:26:24: error: non-floating-point argument in call to __builtin_isnan() +builtin-fp-unop.c:27:27: error: non-floating-point argument in call to __builtin_isnormal() +builtin-fp-unop.c:28:26: error: non-floating-point argument in call to __builtin_signbit() + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/builtin-overflow.c b/usr/src/tools/smatch/src/validation/builtin-overflow.c new file mode 100644 index 0000000000..c3d1d3aa8c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/builtin-overflow.c @@ -0,0 +1,246 @@ +enum e { OK, KO = -1 }; +typedef _Bool bool; + +static int test(int i, long l, long long ll, enum e e, bool b, void *p) +{ + int rc = 0; + + // should be OK + rc += __builtin_add_overflow(i, i, &i); + rc += __builtin_add_overflow(l, i, &i); + rc += __builtin_add_overflow(i, l, &i); + rc += __builtin_add_overflow(i, i, &l); + rc += __builtin_add_overflow(ll, i, &i); + rc += __builtin_add_overflow(i, ll, &i); + rc += __builtin_add_overflow(i, i, &ll); + + rc += __builtin_add_overflow_p(i, i, i); + rc += __builtin_add_overflow_p(l, i, i); + rc += __builtin_add_overflow_p(i, l, i); + rc += __builtin_add_overflow_p(i, i, l); + rc += __builtin_add_overflow_p(ll, i, i); + rc += __builtin_add_overflow_p(i, ll, i); + rc += __builtin_add_overflow_p(i, i, ll); + + rc += __builtin_sub_overflow(i, i, &i); + rc += __builtin_sub_overflow(l, i, &i); + rc += __builtin_sub_overflow(i, l, &i); + rc += __builtin_sub_overflow(i, i, &l); + rc += __builtin_sub_overflow(ll, i, &i); + rc += __builtin_sub_overflow(i, ll, &i); + rc += __builtin_sub_overflow(i, i, &ll); + + rc += __builtin_sub_overflow_p(i, i, i); + rc += __builtin_sub_overflow_p(l, i, i); + rc += __builtin_sub_overflow_p(i, l, i); + rc += __builtin_sub_overflow_p(i, i, l); + rc += __builtin_sub_overflow_p(ll, i, i); + rc += __builtin_sub_overflow_p(i, ll, i); + rc += __builtin_sub_overflow_p(i, i, ll); + + rc += __builtin_mul_overflow(i, i, &i); + rc += __builtin_mul_overflow(l, i, &i); + rc += __builtin_mul_overflow(i, l, &i); + rc += __builtin_mul_overflow(i, i, &l); + rc += __builtin_mul_overflow(ll, i, &i); + rc += __builtin_mul_overflow(i, ll, &i); + rc += __builtin_mul_overflow(i, i, &ll); + + rc += __builtin_mul_overflow_p(i, i, i); + rc += __builtin_mul_overflow_p(l, i, i); + rc += __builtin_mul_overflow_p(i, l, i); + rc += __builtin_mul_overflow_p(i, i, l); + rc += __builtin_mul_overflow_p(ll, i, i); + rc += __builtin_mul_overflow_p(i, ll, i); + rc += __builtin_mul_overflow_p(i, i, ll); + + // should be KO + rc += __builtin_add_overflow(); + rc += __builtin_add_overflow(i); + rc += __builtin_add_overflow(i, i); + rc += __builtin_add_overflow(i, i, &i, i); + rc += __builtin_add_overflow(e, i, &i); + rc += __builtin_add_overflow(i, e, &i); + rc += __builtin_add_overflow(i, i, &e); + rc += __builtin_add_overflow(b, i, &i); + rc += __builtin_add_overflow(i, b, &i); + rc += __builtin_add_overflow(i, i, &b); + rc += __builtin_add_overflow(i, i, p); + + rc += __builtin_add_overflow_p(); + rc += __builtin_add_overflow_p(i); + rc += __builtin_add_overflow_p(i, i); + rc += __builtin_add_overflow_p(i, i, i, i); + rc += __builtin_add_overflow_p(e, i, i); + rc += __builtin_add_overflow_p(i, e, i); + rc += __builtin_add_overflow_p(i, i, e); + rc += __builtin_add_overflow_p(b, i, i); + rc += __builtin_add_overflow_p(i, b, i); + rc += __builtin_add_overflow_p(i, i, b); + rc += __builtin_add_overflow_p(i, i, p); + + rc += __builtin_sub_overflow(); + rc += __builtin_sub_overflow(i); + rc += __builtin_sub_overflow(i, i); + rc += __builtin_sub_overflow(i, i, &i, i); + rc += __builtin_sub_overflow(e, i, &i); + rc += __builtin_sub_overflow(i, e, &i); + rc += __builtin_sub_overflow(i, i, &e); + rc += __builtin_sub_overflow(b, i, &i); + rc += __builtin_sub_overflow(i, b, &i); + rc += __builtin_sub_overflow(i, i, &b); + rc += __builtin_sub_overflow(i, i, p); + + rc += __builtin_sub_overflow_p(); + rc += __builtin_sub_overflow_p(i); + rc += __builtin_sub_overflow_p(i, i); + rc += __builtin_sub_overflow_p(i, i, i, i); + rc += __builtin_sub_overflow_p(e, i, i); + rc += __builtin_sub_overflow_p(i, e, i); + rc += __builtin_sub_overflow_p(i, i, e); + rc += __builtin_sub_overflow_p(b, i, i); + rc += __builtin_sub_overflow_p(i, b, i); + rc += __builtin_sub_overflow_p(i, i, b); + rc += __builtin_sub_overflow_p(i, i, p); + + rc += __builtin_mul_overflow(); + rc += __builtin_mul_overflow(i); + rc += __builtin_mul_overflow(i, i); + rc += __builtin_mul_overflow(i, i, &i, i); + rc += __builtin_mul_overflow(e, i, &i); + rc += __builtin_mul_overflow(i, e, &i); + rc += __builtin_mul_overflow(i, i, &e); + rc += __builtin_mul_overflow(b, i, &i); + rc += __builtin_mul_overflow(i, b, &i); + rc += __builtin_mul_overflow(i, i, &b); + rc += __builtin_mul_overflow(i, i, p); + + rc += __builtin_mul_overflow_p(); + rc += __builtin_mul_overflow_p(i); + rc += __builtin_mul_overflow_p(i, i); + rc += __builtin_mul_overflow_p(i, i, i, i); + rc += __builtin_mul_overflow_p(e, i, i); + rc += __builtin_mul_overflow_p(i, e, i); + rc += __builtin_mul_overflow_p(i, i, e); + rc += __builtin_mul_overflow_p(b, i, i); + rc += __builtin_mul_overflow_p(i, b, i); + rc += __builtin_mul_overflow_p(i, i, b); + rc += __builtin_mul_overflow_p(i, i, p); + + return rc; +} + +/* + * check-name: builtin-overflow + * + * check-error-start +builtin-overflow.c:58:37: error: not enough arguments for __builtin_add_overflow +builtin-overflow.c:59:37: error: not enough arguments for __builtin_add_overflow +builtin-overflow.c:60:37: error: not enough arguments for __builtin_add_overflow +builtin-overflow.c:61:37: error: too many arguments for __builtin_add_overflow +builtin-overflow.c:62:38: error: invalid type for argument 1: +builtin-overflow.c:62:38: int enum e e +builtin-overflow.c:63:41: error: invalid type for argument 2: +builtin-overflow.c:63:41: int enum e e +builtin-overflow.c:64:45: error: invalid type for argument 3: +builtin-overflow.c:64:45: int enum e * +builtin-overflow.c:65:38: error: invalid type for argument 1: +builtin-overflow.c:65:38: bool [usertype] b +builtin-overflow.c:66:41: error: invalid type for argument 2: +builtin-overflow.c:66:41: bool [usertype] b +builtin-overflow.c:67:45: error: invalid type for argument 3: +builtin-overflow.c:67:45: bool * +builtin-overflow.c:68:44: error: invalid type for argument 3: +builtin-overflow.c:68:44: void *p +builtin-overflow.c:70:39: error: not enough arguments for __builtin_add_overflow_p +builtin-overflow.c:71:39: error: not enough arguments for __builtin_add_overflow_p +builtin-overflow.c:72:39: error: not enough arguments for __builtin_add_overflow_p +builtin-overflow.c:73:39: error: too many arguments for __builtin_add_overflow_p +builtin-overflow.c:74:40: error: invalid type for argument 1: +builtin-overflow.c:74:40: int enum e [addressable] e +builtin-overflow.c:75:43: error: invalid type for argument 2: +builtin-overflow.c:75:43: int enum e [addressable] e +builtin-overflow.c:76:46: error: invalid type for argument 3: +builtin-overflow.c:76:46: int enum e [addressable] e +builtin-overflow.c:77:40: error: invalid type for argument 1: +builtin-overflow.c:77:40: bool [addressable] [usertype] b +builtin-overflow.c:78:43: error: invalid type for argument 2: +builtin-overflow.c:78:43: bool [addressable] [usertype] b +builtin-overflow.c:79:46: error: invalid type for argument 3: +builtin-overflow.c:79:46: bool [addressable] [usertype] b +builtin-overflow.c:80:46: error: invalid type for argument 3: +builtin-overflow.c:80:46: void *p +builtin-overflow.c:82:37: error: not enough arguments for __builtin_sub_overflow +builtin-overflow.c:83:37: error: not enough arguments for __builtin_sub_overflow +builtin-overflow.c:84:37: error: not enough arguments for __builtin_sub_overflow +builtin-overflow.c:85:37: error: too many arguments for __builtin_sub_overflow +builtin-overflow.c:86:38: error: invalid type for argument 1: +builtin-overflow.c:86:38: int enum e [addressable] e +builtin-overflow.c:87:41: error: invalid type for argument 2: +builtin-overflow.c:87:41: int enum e [addressable] e +builtin-overflow.c:88:45: error: invalid type for argument 3: +builtin-overflow.c:88:45: int enum e * +builtin-overflow.c:89:38: error: invalid type for argument 1: +builtin-overflow.c:89:38: bool [addressable] [usertype] b +builtin-overflow.c:90:41: error: invalid type for argument 2: +builtin-overflow.c:90:41: bool [addressable] [usertype] b +builtin-overflow.c:91:45: error: invalid type for argument 3: +builtin-overflow.c:91:45: bool * +builtin-overflow.c:92:44: error: invalid type for argument 3: +builtin-overflow.c:92:44: void *p +builtin-overflow.c:94:39: error: not enough arguments for __builtin_sub_overflow_p +builtin-overflow.c:95:39: error: not enough arguments for __builtin_sub_overflow_p +builtin-overflow.c:96:39: error: not enough arguments for __builtin_sub_overflow_p +builtin-overflow.c:97:39: error: too many arguments for __builtin_sub_overflow_p +builtin-overflow.c:98:40: error: invalid type for argument 1: +builtin-overflow.c:98:40: int enum e [addressable] e +builtin-overflow.c:99:43: error: invalid type for argument 2: +builtin-overflow.c:99:43: int enum e [addressable] e +builtin-overflow.c:100:46: error: invalid type for argument 3: +builtin-overflow.c:100:46: int enum e [addressable] e +builtin-overflow.c:101:40: error: invalid type for argument 1: +builtin-overflow.c:101:40: bool [addressable] [usertype] b +builtin-overflow.c:102:43: error: invalid type for argument 2: +builtin-overflow.c:102:43: bool [addressable] [usertype] b +builtin-overflow.c:103:46: error: invalid type for argument 3: +builtin-overflow.c:103:46: bool [addressable] [usertype] b +builtin-overflow.c:104:46: error: invalid type for argument 3: +builtin-overflow.c:104:46: void *p +builtin-overflow.c:106:37: error: not enough arguments for __builtin_mul_overflow +builtin-overflow.c:107:37: error: not enough arguments for __builtin_mul_overflow +builtin-overflow.c:108:37: error: not enough arguments for __builtin_mul_overflow +builtin-overflow.c:109:37: error: too many arguments for __builtin_mul_overflow +builtin-overflow.c:110:38: error: invalid type for argument 1: +builtin-overflow.c:110:38: int enum e [addressable] e +builtin-overflow.c:111:41: error: invalid type for argument 2: +builtin-overflow.c:111:41: int enum e [addressable] e +builtin-overflow.c:112:45: error: invalid type for argument 3: +builtin-overflow.c:112:45: int enum e * +builtin-overflow.c:113:38: error: invalid type for argument 1: +builtin-overflow.c:113:38: bool [addressable] [usertype] b +builtin-overflow.c:114:41: error: invalid type for argument 2: +builtin-overflow.c:114:41: bool [addressable] [usertype] b +builtin-overflow.c:115:45: error: invalid type for argument 3: +builtin-overflow.c:115:45: bool * +builtin-overflow.c:116:44: error: invalid type for argument 3: +builtin-overflow.c:116:44: void *p +builtin-overflow.c:118:39: error: not enough arguments for __builtin_mul_overflow_p +builtin-overflow.c:119:39: error: not enough arguments for __builtin_mul_overflow_p +builtin-overflow.c:120:39: error: not enough arguments for __builtin_mul_overflow_p +builtin-overflow.c:121:39: error: too many arguments for __builtin_mul_overflow_p +builtin-overflow.c:122:40: error: invalid type for argument 1: +builtin-overflow.c:122:40: int enum e [addressable] e +builtin-overflow.c:123:43: error: invalid type for argument 2: +builtin-overflow.c:123:43: int enum e [addressable] e +builtin-overflow.c:124:46: error: invalid type for argument 3: +builtin-overflow.c:124:46: int enum e [addressable] e +builtin-overflow.c:125:40: error: invalid type for argument 1: +builtin-overflow.c:125:40: bool [addressable] [usertype] b +builtin-overflow.c:126:43: error: invalid type for argument 2: +builtin-overflow.c:126:43: bool [addressable] [usertype] b +builtin-overflow.c:127:46: error: invalid type for argument 3: +builtin-overflow.c:127:46: bool [addressable] [usertype] b +builtin-overflow.c:128:46: error: invalid type for argument 3: +builtin-overflow.c:128:46: void *p + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/builtin-prototype.c b/usr/src/tools/smatch/src/validation/builtin-prototype.c new file mode 100644 index 0000000000..d713db45c3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/builtin-prototype.c @@ -0,0 +1,15 @@ +void memcpy(void *dst, const void *src, unsigned int size); +void memcpy(void *dst, const void *src, unsigned int size) +{ + __builtin_memcpy(dst, src, size); +} + +unsigned int strlen(const char *src); +unsigned int strlen(const char *src) +{ + return __builtin_strlen(src); +} + +/* + * check-name: builtin-prototype + */ diff --git a/usr/src/tools/smatch/src/validation/c11-alignas.c b/usr/src/tools/smatch/src/validation/c11-alignas.c index 4b264a5dc2..b7ae2abc3b 100644 --- a/usr/src/tools/smatch/src/validation/c11-alignas.c +++ b/usr/src/tools/smatch/src/validation/c11-alignas.c @@ -36,5 +36,5 @@ c11-alignas.c:10:17: error: Syntax error in unary expression * check-error-end * * check-output-ignore - * check-output-contains: ret\\.32 *\$0 + * check-output-contains: ret\\.32 *\\$0 */ diff --git a/usr/src/tools/smatch/src/validation/c11-alignof.c b/usr/src/tools/smatch/src/validation/c11-alignof.c index 238ef9941e..83c0e70d9d 100644 --- a/usr/src/tools/smatch/src/validation/c11-alignof.c +++ b/usr/src/tools/smatch/src/validation/c11-alignof.c @@ -8,5 +8,5 @@ static int foo(void) * check-command: test-linearize -std=c11 $file * * check-output-ignore - * check-output-contains: ret\\.32 *\$2 + * check-output-contains: ret\\.32 *\\$2 */ diff --git a/usr/src/tools/smatch/src/validation/c11-atomic.c b/usr/src/tools/smatch/src/validation/c11-atomic.c new file mode 100644 index 0000000000..e87d06cd11 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/c11-atomic.c @@ -0,0 +1,93 @@ +void f00(int _Atomic dst); +void f01(int _Atomic *dst); +void f02(int _Atomic *dst); +void f03(int _Atomic *dst); + +int _Atomic qo; +int uo; + +void f00(int dst) { } /* check-should-pass */ +void f01(typeof(&qo) dst) { } /* check-should-pass */ +void f02(int *dst) { } /* check-should-fail */ +void f03(typeof(&uo) dst) { } /* check-should-fail */ + +void foo(void) +{ + qo = uo; /* check-should-pass */ + uo = qo; /* check-should-pass */ +} + +void ref(void) +{ + const int qo; + int uo; + extern const int *pqo; + extern int *puo; + + pqo = &qo; /* check-should-pass */ + pqo = &uo; /* check-should-pass */ + pqo = puo; + + puo = &uo; /* check-should-pass */ + + puo = &qo; /* check-should-fail */ + puo = pqo; /* check-should-fail */ +} + +void bar(void) +{ + extern int _Atomic *pqo; + extern int *puo; + + pqo = &qo; /* check-should-pass */ + pqo = &uo; /* check-should-pass */ + pqo = puo; + + puo = &uo; /* check-should-pass */ + + puo = &qo; /* check-should-fail */ + puo = pqo; /* check-should-fail */ +} + +void baz(void) +{ + extern typeof(&qo) pqo; + extern typeof(&uo) puo; + + pqo = &qo; /* check-should-pass */ + pqo = &uo; /* check-should-pass */ + pqo = puo; + + puo = &uo; /* check-should-pass */ + + puo = &qo; /* check-should-fail */ + puo = pqo; /* check-should-fail */ +} + +/* + * check-name: C11 _Atomic type qualifier + * check-command: sparse -Wno-decl $file; + * + * check-error-start +c11-atomic.c:11:6: error: symbol 'f02' redeclared with different type (originally declared at c11-atomic.c:3) - incompatible argument 1 (different modifiers) +c11-atomic.c:12:6: error: symbol 'f03' redeclared with different type (originally declared at c11-atomic.c:4) - incompatible argument 1 (different modifiers) +c11-atomic.c:33:13: warning: incorrect type in assignment (different modifiers) +c11-atomic.c:33:13: expected int *extern [assigned] puo +c11-atomic.c:33:13: got int const * +c11-atomic.c:34:13: warning: incorrect type in assignment (different modifiers) +c11-atomic.c:34:13: expected int *extern [assigned] puo +c11-atomic.c:34:13: got int const *extern [assigned] pqo +c11-atomic.c:48:13: warning: incorrect type in assignment (different modifiers) +c11-atomic.c:48:13: expected int *extern [assigned] puo +c11-atomic.c:48:13: got int [atomic] * +c11-atomic.c:49:13: warning: incorrect type in assignment (different modifiers) +c11-atomic.c:49:13: expected int *extern [assigned] puo +c11-atomic.c:49:13: got int [atomic] *extern [assigned] pqo +c11-atomic.c:63:13: warning: incorrect type in assignment (different modifiers) +c11-atomic.c:63:13: expected int *extern [assigned] puo +c11-atomic.c:63:13: got int [atomic] * +c11-atomic.c:64:13: warning: incorrect type in assignment (different modifiers) +c11-atomic.c:64:13: expected int *extern [assigned] puo +c11-atomic.c:64:13: got int [atomic] *extern [assigned] pqo + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/c11-noreturn.c b/usr/src/tools/smatch/src/validation/c11-noreturn.c index bc20de82f9..d20a2249b8 100644 --- a/usr/src/tools/smatch/src/validation/c11-noreturn.c +++ b/usr/src/tools/smatch/src/validation/c11-noreturn.c @@ -5,5 +5,5 @@ static _Noreturn void foo(void) { while (1) ; } * check-command: test-parsing -std=c11 $file * * check-output-ignore - * check-output-contains: \[noreturn\] + * check-output-contains: \\[noreturn\\] */ diff --git a/usr/src/tools/smatch/src/validation/c11-thread-local.c b/usr/src/tools/smatch/src/validation/c11-thread-local.c index 464c3e16a6..db848de630 100644 --- a/usr/src/tools/smatch/src/validation/c11-thread-local.c +++ b/usr/src/tools/smatch/src/validation/c11-thread-local.c @@ -5,5 +5,5 @@ static _Thread_local int foo; * check-command: test-parsing -std=c11 $file * * check-output-ignore - * check-output-contains: \[tls\] + * check-output-contains: \\[tls\\] */ diff --git a/usr/src/tools/smatch/src/validation/call-inlined.c b/usr/src/tools/smatch/src/validation/call-inlined.c new file mode 100644 index 0000000000..3612c5c426 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/call-inlined.c @@ -0,0 +1,55 @@ +static const char messg[] = "def"; + +static inline int add(int a, int b) +{ + return a + b; +} + +int foo(int a, int b) { return add(a + b, 1); } +void bar(int a, int b) { add(a + b, 1); } + + +static inline const char *lstrip(const char *str) +{ + return str + 1; +} + +const char *bas(void) { return lstrip("abc"); } +const char *qus(void) { return lstrip(messg); } + +/* + * check-name: call-inlined + * check-command: test-linearize -Wno-decl -m64 $file + * check-assert: sizeof(void*) == 8 + * + * check-output-start +foo: +.L0: + <entry-point> + add.32 %r3 <- %arg1, %arg2 + add.32 %r5 <- %r3, $1 + ret.32 %r5 + + +bar: +.L3: + <entry-point> + ret + + +bas: +.L6: + <entry-point> + add.64 %r16 <- "abc", $1 + ret.64 %r16 + + +qus: +.L9: + <entry-point> + add.64 %r21 <- messg, $1 + ret.64 %r21 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/call-variadic.c b/usr/src/tools/smatch/src/validation/call-variadic.c new file mode 100644 index 0000000000..e5a9925b51 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/call-variadic.c @@ -0,0 +1,23 @@ +#define NULL ((void*)0) + +extern int print(const char *msg, ...); + +int foo(const char *fmt, int a, long l, int *p) +{ + return print("msg %c: %d %d/%ld %ld/%p %p\n", 'x', a, __LINE__, l, 0L, p, NULL); +} + +/* + * check-name: call-variadic + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + call.32 %r5 <- print, "msg %c: %d %d/%ld %ld/%p %p\n", $120, %arg2, $7, %arg3, $0, %arg4, $0 + ret.32 %r5 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/cast-bad-00.c b/usr/src/tools/smatch/src/validation/cast-bad-00.c new file mode 100644 index 0000000000..6d15485d40 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/cast-bad-00.c @@ -0,0 +1,47 @@ +typedef unsigned short u16; +typedef unsigned int u32; + +union u { + u32 a; + u16 b; +}; + +struct s { + u32 a; + u16 b; +}; + + +void bar(u16, u32); +void union_to_int(u16 val); +void struct_to_int(u16 val); + + +void union_to_int(u16 val) +{ + union u u; + + u.b = val; + bar(u.b, u); +} + +void struct_to_int(u16 val) +{ + struct s s; + + s.b = val; + bar(s.b, s); +} + +/* + * check-name: cast-bad 00 + * + * check-error-start +cast-bad-00.c:25:18: warning: incorrect type in argument 2 (different base types) +cast-bad-00.c:25:18: expected unsigned int [usertype] +cast-bad-00.c:25:18: got union u [assigned] u +cast-bad-00.c:33:18: warning: incorrect type in argument 2 (different base types) +cast-bad-00.c:33:18: expected unsigned int [usertype] +cast-bad-00.c:33:18: got struct s [assigned] s + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/cast-bad-01.c b/usr/src/tools/smatch/src/validation/cast-bad-01.c new file mode 100644 index 0000000000..4a7a397ea4 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/cast-bad-01.c @@ -0,0 +1,13 @@ +extern unsigned long l; + +int foo(void) { + return (int) (typeof(fundecl(0))) l; +} + +/* + * check-name: cast-bad 01 + * + * check-error-start +cast-bad-01.c:4:30: error: undefined identifier 'fundecl' + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/cast-kinds-check.c b/usr/src/tools/smatch/src/validation/cast-kinds-check.c new file mode 100644 index 0000000000..0c0cd67368 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/cast-kinds-check.c @@ -0,0 +1,20 @@ +#include "optim/cast-kinds.c" + +/* + * check-name: cast-kinds check + * check-command: sparse -m64 -v -Wno-pointer-to-int-cast $file + * check-assert: sizeof(long) == 8 + * + * check-error-start +optim/cast-kinds.c:5:45: warning: cast drops bits +optim/cast-kinds.c:6:47: warning: cast drops bits +optim/cast-kinds.c:7:46: warning: cast drops bits +optim/cast-kinds.c:8:45: warning: cast drops bits +optim/cast-kinds.c:12:48: warning: cast drops bits +optim/cast-kinds.c:13:50: warning: cast drops bits +optim/cast-kinds.c:14:49: warning: cast drops bits +optim/cast-kinds.c:15:48: warning: cast drops bits +optim/cast-kinds.c:37:48: warning: non size-preserving integer to pointer cast +optim/cast-kinds.c:38:50: warning: non size-preserving integer to pointer cast + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/cast-weirds.c b/usr/src/tools/smatch/src/validation/cast-weirds.c new file mode 100644 index 0000000000..a753c2989c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/cast-weirds.c @@ -0,0 +1,19 @@ +typedef unsigned int uint; +typedef unsigned long ulong; + +static int * int_2_iptr(int a) { return (int *)a; } +static int * uint_2_iptr(uint a) { return (int *)a; } + +static void * int_2_vptr(int a) { return (void *)a; } +static void * uint_2_vptr(uint a) { return (void *)a; } + +/* + * check-name: cast-weirds + * check-command: sparse -m64 $file + * check-assert: sizeof(void *) == 8 + * + * check-error-start +cast-weirds.c:4:48: warning: non size-preserving integer to pointer cast +cast-weirds.c:5:50: warning: non size-preserving integer to pointer cast + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/char-signed.c b/usr/src/tools/smatch/src/validation/char-signed.c new file mode 100644 index 0000000000..7f657dacb3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/char-signed.c @@ -0,0 +1,9 @@ +void foo(void) +{ + _Static_assert((char) -1 == -1, "plain char is not signed"); +} + +/* + * check-name: fsigned-char + * check-command: sparse -fsigned-char -Wno-decl $file + */ diff --git a/usr/src/tools/smatch/src/validation/char-unsigned.c b/usr/src/tools/smatch/src/validation/char-unsigned.c new file mode 100644 index 0000000000..19cadbda32 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/char-unsigned.c @@ -0,0 +1,11 @@ +#define MASK ((1 << __CHAR_BIT__) - 1) + +void foo(void) +{ + _Static_assert((char) -1 == (-1 & MASK), "plain char is not unsigned"); +} + +/* + * check-name: fsigned-char + * check-command: sparse -funsigned-char -Wno-decl $file + */ diff --git a/usr/src/tools/smatch/src/validation/check_access-multi.c b/usr/src/tools/smatch/src/validation/check_access-multi.c new file mode 100644 index 0000000000..e0ee468258 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/check_access-multi.c @@ -0,0 +1,15 @@ +extern int *a; +extern int b[1]; + +static void foo(void) +{ + *a = b[1]; +} + +/* + * check-name: check_access-multi + * + * check-error-start +check_access-multi.c:6:15: warning: invalid access past the end of 'b' (4 4) + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/check_access-store.c b/usr/src/tools/smatch/src/validation/check_access-store.c new file mode 100644 index 0000000000..489507fd7b --- /dev/null +++ b/usr/src/tools/smatch/src/validation/check_access-store.c @@ -0,0 +1,21 @@ +extern int a[1]; + +static int r(void) +{ + return a[1]; +} + +static void w(void) +{ + a[1] = 2; +} + +/* + * check-name: check_access-store + * check-known-to-fail + * + * check-error-start +check_access-store.c:5:17: warning: invalid access past the end of 'a' (4 4) +check_access-store.c:10:17: warning: invalid access past the end of 'a' (4 4) + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/check_byte_count-ice.c b/usr/src/tools/smatch/src/validation/check_byte_count-ice.c index 7b85b96319..88783865f1 100644 --- a/usr/src/tools/smatch/src/validation/check_byte_count-ice.c +++ b/usr/src/tools/smatch/src/validation/check_byte_count-ice.c @@ -8,12 +8,12 @@ static void foo(void *a) * check-name: Segfault in check_byte_count after syntax error * * check-error-start -check_byte_count-ice.c:6:0: warning: Newline in string or character constant +check_byte_count-ice.c:6:0: warning: missing terminating ' character check_byte_count-ice.c:5:23: warning: multi-character character constant check_byte_count-ice.c:6:1: error: Expected ) in function call check_byte_count-ice.c:6:1: error: got } -builtin:0:0: error: Expected } at end of function -builtin:0:0: error: got end-of-input +check_byte_count-ice.c:20:0: error: Expected } at end of function +check_byte_count-ice.c:20:0: error: got end-of-input check_byte_count-ice.c:5:15: error: not enough arguments for function memset * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/choose_expr.c b/usr/src/tools/smatch/src/validation/choose_expr.c index f6fd84cf2a..b5d2b4e6fc 100644 --- a/usr/src/tools/smatch/src/validation/choose_expr.c +++ b/usr/src/tools/smatch/src/validation/choose_expr.c @@ -7,11 +7,11 @@ static int z = 1/(sizeof(__builtin_choose_expr(1,s,0)) - 42); * check-name: choose expr builtin * check-error-start choose_expr.c:1:51: warning: incorrect type in initializer (different base types) -choose_expr.c:1:51: expected int static [signed] [toplevel] x -choose_expr.c:1:51: got void <noident> +choose_expr.c:1:51: expected int static [toplevel] x +choose_expr.c:1:51: got void choose_expr.c:2:41: warning: incorrect type in initializer (different base types) -choose_expr.c:2:41: expected int static [signed] [toplevel] y -choose_expr.c:2:41: got char *<noident> +choose_expr.c:2:41: expected int static [toplevel] y +choose_expr.c:2:41: got char * choose_expr.c:4:17: warning: division by zero * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/compound-assign-type.c b/usr/src/tools/smatch/src/validation/compound-assign-type.c index 450fa26d9a..2e06eba253 100644 --- a/usr/src/tools/smatch/src/validation/compound-assign-type.c +++ b/usr/src/tools/smatch/src/validation/compound-assign-type.c @@ -7,9 +7,12 @@ static unsigned int foo(unsigned int x, long a) /* * check-name: compound-assign-type * check-command: test-linearize -m64 $file + * check-assert: sizeof(long) == 8 + * * check-output-ignore * * check-output-excludes: divu\\.32 * check-output-contains: divs\\.64 - * check-output-contains: scast\\.32 + * check-output-contains: zext.64 .* (32) %arg1 + * check-output-contains: trunc.32 .* (64) */ diff --git a/usr/src/tools/smatch/src/validation/compound-sizes.c b/usr/src/tools/smatch/src/validation/compound-sizes.c new file mode 100644 index 0000000000..d8ccf6052a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/compound-sizes.c @@ -0,0 +1,88 @@ +// This tests sparse "-vcompound" output. +#define NULL ((void*)0) +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +// Do not list functions. +static int do_nothing(void) +{} + +// no: +static inline int zero(void) +{ + return 0 / 1; +} + +// no: +struct inventory { + unsigned char description[64]; + unsigned char department[64]; + uint32_t dept_number; + uint32_t item_cost; + uint64_t stock_number; + uint32_t tally[12]; // per month +}; + +// no +static struct inventory *get_inv(uint64_t stocknum) +{ + return NULL; +} + +// no +union un { + struct inventory inv; + unsigned char bytes[0]; +}; + +// yes +static union un un; + +// yes +static struct inventory inven[100]; + +// no +typedef struct inventory inventory_t; + +// no +static struct inventory *invptr; + +// yes +static inventory_t invent[10]; + +// no +static float floater; +static double double_float; + +// yes +static float floats[42]; +static double doubles[84]; + +// no +int main(void) +{ + // no, these are not global. + struct inventory inv[10]; + inventory_t invt[10]; + // what about statics? + static struct inventory invtop; + static inventory_t inv_top; + static uint64_t stocknums[100]; + + invptr = get_inv(42000); + return 0; +} + +/* + * check-name: compound-sizes + * check-command: sparse -vcompound $file + * check-assert: _Alignof(long long) == 8 + * + * check-error-start +compound-sizes.c:39:17: union un static [toplevel] un: compound size 192, alignment 8 +compound-sizes.c:42:25: struct inventory static [toplevel] inven[100]: compound size 19200, alignment 8 +compound-sizes.c:51:33: struct inventory static [toplevel] [usertype] invent[10]: compound size 1920, alignment 8 +compound-sizes.c:58:25: float static [toplevel] floats[42]: compound size 168, alignment 4 +compound-sizes.c:59:25: double static [toplevel] doubles[84]: compound size 672, alignment 8 + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/cond-address-array.c b/usr/src/tools/smatch/src/validation/cond-address-array.c deleted file mode 100644 index e1d2f87f87..0000000000 --- a/usr/src/tools/smatch/src/validation/cond-address-array.c +++ /dev/null @@ -1,26 +0,0 @@ -int foo(void) { - extern int a[]; - - if (a) - return 1; - return 0; -} - -int bar(void) { - int a[2]; - - if (a) - return 1; - return 0; -} - -/* - * check-name: cond-address-array.c - * check-command: test-linearize -Wno-decl -Waddress $file - * check-output-ignore - * - * check-error-start -cond-address-array.c:4:13: warning: the address of an array will always evaluate as true -cond-address-array.c:12:13: warning: the address of an array will always evaluate as true - * check-error-end - */ diff --git a/usr/src/tools/smatch/src/validation/cond-address-function.c b/usr/src/tools/smatch/src/validation/cond-address-function.c deleted file mode 100644 index 9a143a0091..0000000000 --- a/usr/src/tools/smatch/src/validation/cond-address-function.c +++ /dev/null @@ -1,18 +0,0 @@ -extern void func(void); - -int global_function(void) -{ - if (func) - return 1; - return 0; -} - -/* - * check-name: cond-address-function - * check-command: test-linearize -Wno-decl -Waddress $file - * check-output-ignore - * - * check-error-start -cond-address-function.c:5:13: warning: the address of a function will always evaluate as true - * check-error-end - */ diff --git a/usr/src/tools/smatch/src/validation/cond-address.c b/usr/src/tools/smatch/src/validation/cond-address.c index 2a69f4b922..c9253ad04e 100644 --- a/usr/src/tools/smatch/src/validation/cond-address.c +++ b/usr/src/tools/smatch/src/validation/cond-address.c @@ -10,5 +10,5 @@ int qux(void) { if (f && a) return 1; return 0; } * check-command: test-linearize -Wno-decl $file * check-output-ignore * - * check-excludes: VOID + * check-output-excludes: VOID */ diff --git a/usr/src/tools/smatch/src/validation/cond-err-expand.c b/usr/src/tools/smatch/src/validation/cond-err-expand.c index 93bbac1538..b52624bc9d 100644 --- a/usr/src/tools/smatch/src/validation/cond-err-expand.c +++ b/usr/src/tools/smatch/src/validation/cond-err-expand.c @@ -18,10 +18,14 @@ void bar(void) * check-command: test-linearize -Wno-decl $file * * check-error-start -cond-err-expand.c:8:11: error: incompatible types in conditional expression (different base types) -cond-err-expand.c:13:11: error: incompatible types in conditional expression (different base types) +cond-err-expand.c:8:11: error: incompatible types in conditional expression (different base types): +cond-err-expand.c:8:11: int +cond-err-expand.c:8:11: void +cond-err-expand.c:13:11: error: incompatible types in conditional expression (different base types): +cond-err-expand.c:13:11: void +cond-err-expand.c:13:11: int * check-error-end * * check-output-ignore - * check-excludes: call.* __builtin_constant_p + * check-output-excludes: call.* __builtin_constant_p */ diff --git a/usr/src/tools/smatch/src/validation/conditional-type.c b/usr/src/tools/smatch/src/validation/conditional-type.c index a14c05ec1d..9126721277 100644 --- a/usr/src/tools/smatch/src/validation/conditional-type.c +++ b/usr/src/tools/smatch/src/validation/conditional-type.c @@ -79,21 +79,21 @@ static int good_if_ptr(void *ptr) * check-name: conditional-type * * check-error-start -conditional-type.c:18:18: error: incorrect type in conditional +conditional-type.c:18:18: error: incorrect type in conditional (non-scalar type) conditional-type.c:18:18: got void -conditional-type.c:19:13: error: incorrect type in conditional +conditional-type.c:19:13: error: incorrect type in conditional (non-scalar type) conditional-type.c:19:13: got struct state s -conditional-type.c:24:18: error: incorrect type in conditional +conditional-type.c:24:18: error: incorrect type in conditional (non-scalar type) conditional-type.c:24:18: got void -conditional-type.c:29:21: error: incorrect type in conditional +conditional-type.c:29:21: error: incorrect type in conditional (non-scalar type) conditional-type.c:29:21: got void -conditional-type.c:30:16: error: incorrect type in conditional +conditional-type.c:30:16: error: incorrect type in conditional (non-scalar type) conditional-type.c:30:16: got struct state s -conditional-type.c:34:21: error: incorrect type in conditional +conditional-type.c:34:21: error: incorrect type in conditional (non-scalar type) conditional-type.c:34:21: got void -conditional-type.c:36:20: error: incorrect type in conditional +conditional-type.c:36:20: error: incorrect type in conditional (non-scalar type) conditional-type.c:36:20: got void -conditional-type.c:40:21: error: incorrect type in conditional +conditional-type.c:40:21: error: incorrect type in conditional (non-scalar type) conditional-type.c:40:21: got void * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/constant-suffix-64.c b/usr/src/tools/smatch/src/validation/constant-suffix-64.c index e65706b024..6722633185 100644 --- a/usr/src/tools/smatch/src/validation/constant-suffix-64.c +++ b/usr/src/tools/smatch/src/validation/constant-suffix-64.c @@ -7,6 +7,7 @@ static unsigned long b = BIGUL; /* * check-name: constant-suffix * check-command: sparse -m64 -Wconstant-suffix $file + * check-assert: sizeof(long) == 8 * * check-error-start constant-suffix-64.c:4:26: warning: constant 0xfffff00000000000U is so big it is unsigned long diff --git a/usr/src/tools/smatch/src/validation/constexpr-addr-of-static-member.c b/usr/src/tools/smatch/src/validation/constexpr-addr-of-static-member.c index f944f213e9..c2a74ac78b 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-addr-of-static-member.c +++ b/usr/src/tools/smatch/src/validation/constexpr-addr-of-static-member.c @@ -18,7 +18,7 @@ static int *f = &a.d.b[1]; // OK static int *g = &(&a.d)->b[1]; // OK /* - * check-name: address of static object's member constness verification. + * check-name: constexpr static object's member address * check-command: sparse -Wconstexpr-not-const $file * * check-error-start diff --git a/usr/src/tools/smatch/src/validation/constexpr-addr-of-static.c b/usr/src/tools/smatch/src/validation/constexpr-addr-of-static.c index a3af99ae72..1af9438c60 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-addr-of-static.c +++ b/usr/src/tools/smatch/src/validation/constexpr-addr-of-static.c @@ -25,7 +25,7 @@ static void m(void) { } /* - * check-name: address of static object constness verification. + * check-name: constexpr static object address * check-command: sparse -Wconstexpr-not-const $file * * check-error-start diff --git a/usr/src/tools/smatch/src/validation/constexpr-binop.c b/usr/src/tools/smatch/src/validation/constexpr-binop.c index 85a88e3cd1..01261de9ce 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-binop.c +++ b/usr/src/tools/smatch/src/validation/constexpr-binop.c @@ -19,7 +19,7 @@ static int a[] = { }; /* - * check-name: Expression constness propagation in binops and alike + * check-name: constexprness in binops and alike * * check-error-start constexpr-binop.c:3:12: error: bad constant expression diff --git a/usr/src/tools/smatch/src/validation/constexpr-cast.c b/usr/src/tools/smatch/src/validation/constexpr-cast.c index 27069614c2..993c46675d 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-cast.c +++ b/usr/src/tools/smatch/src/validation/constexpr-cast.c @@ -14,7 +14,7 @@ static int a[] = { }; /* - * check-name: Expression constness propagation in casts + * check-name: constexprness in casts * * check-error-start constexpr-cast.c:9:11: error: bad integer constant expression diff --git a/usr/src/tools/smatch/src/validation/constexpr-compound-literal.c b/usr/src/tools/smatch/src/validation/constexpr-compound-literal.c index d7f21ad7a0..e137873955 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-compound-literal.c +++ b/usr/src/tools/smatch/src/validation/constexpr-compound-literal.c @@ -9,7 +9,7 @@ static void foo(void) } /* - * check-name: compound literal address constness verification + * check-name: constexpr compound literal address * check-command: sparse -Wconstexpr-not-const $file * * check-error-start diff --git a/usr/src/tools/smatch/src/validation/constexpr-conditional.c b/usr/src/tools/smatch/src/validation/constexpr-conditional.c index a3331b3ef0..0f2409bf2e 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-conditional.c +++ b/usr/src/tools/smatch/src/validation/constexpr-conditional.c @@ -21,7 +21,7 @@ static int a[] = { }; /* - * check-name: Expression constness propagation in conditional expressions + * check-name: constexprness in conditionals * * check-error-start constexpr-conditional.c:12:13: error: bad constant expression diff --git a/usr/src/tools/smatch/src/validation/constexpr-init.c b/usr/src/tools/smatch/src/validation/constexpr-init.c index d7e7a450f5..c182fe79cb 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-init.c +++ b/usr/src/tools/smatch/src/validation/constexpr-init.c @@ -39,7 +39,7 @@ static void s(void) { } /* - * check-name: static storage object initializer constness verification. + * check-name: constexprness static storage object initializer * check-command: sparse -Wconstexpr-not-const $file * * check-error-start diff --git a/usr/src/tools/smatch/src/validation/constexpr-labelref.c b/usr/src/tools/smatch/src/validation/constexpr-labelref.c index 15b5293aeb..93dc5ccc5e 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-labelref.c +++ b/usr/src/tools/smatch/src/validation/constexpr-labelref.c @@ -6,7 +6,7 @@ label1: } /* - * check-name: label reference constness verification. + * check-name: constexprness label reference * check-command: sparse -Wconstexpr-not-const $file * * check-error-start diff --git a/usr/src/tools/smatch/src/validation/constexpr-offsetof.c b/usr/src/tools/smatch/src/validation/constexpr-offsetof.c index d1697b0c5c..f55cb5639b 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-offsetof.c +++ b/usr/src/tools/smatch/src/validation/constexpr-offsetof.c @@ -13,7 +13,7 @@ static int o[] = { }; /* - * check-name: __builtin_offsetof() constness verification. + * check-name: constexprness __builtin_offsetof() * * check-error-start constexpr-offsetof.c:12:39: error: bad constant expression diff --git a/usr/src/tools/smatch/src/validation/constexpr-pointer-arith.c b/usr/src/tools/smatch/src/validation/constexpr-pointer-arith.c index a92202800a..f27d117a0c 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-pointer-arith.c +++ b/usr/src/tools/smatch/src/validation/constexpr-pointer-arith.c @@ -17,7 +17,7 @@ static int *o = &*(b + 1); // OK static int *p = &*(d + 1); // KO /* - * check-name: pointer arithmetic constness verification. + * check-name: consrexprness pointer arithmetic * check-command: sparse -Wconstexpr-not-const $file * * check-error-start diff --git a/usr/src/tools/smatch/src/validation/constexpr-pointer-cast.c b/usr/src/tools/smatch/src/validation/constexpr-pointer-cast.c index d19c10828d..bdf668c1ac 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-pointer-cast.c +++ b/usr/src/tools/smatch/src/validation/constexpr-pointer-cast.c @@ -4,7 +4,7 @@ static int *c = (int*)b; // KO /* - * check-name: integer literal cast to pointer type constness verification. + * check-name: constexprness integer literal cast to pointer type * check-command: sparse -Wconstexpr-not-const $file * * check-error-start diff --git a/usr/src/tools/smatch/src/validation/constexpr-preop.c b/usr/src/tools/smatch/src/validation/constexpr-preop.c index 5d869da4f7..3fd5774594 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-preop.c +++ b/usr/src/tools/smatch/src/validation/constexpr-preop.c @@ -16,7 +16,7 @@ static int a[] = { }; /* - * check-name: Expression constness propagation in preops + * check-name: constexprness in preops * * check-error-start constexpr-preop.c:4:5: error: bad constant expression @@ -25,5 +25,7 @@ constexpr-preop.c:8:4: error: bad constant expression constexpr-preop.c:9:4: error: bad constant expression constexpr-preop.c:14:4: error: bad integer constant expression constexpr-preop.c:15:4: error: bad integer constant expression +constexpr-preop.c:10:4: error: index out of bounds in initializer +constexpr-preop.c:11:4: error: index out of bounds in initializer * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/constexpr-shift.c b/usr/src/tools/smatch/src/validation/constexpr-shift.c new file mode 100644 index 0000000000..df01b74e8d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/constexpr-shift.c @@ -0,0 +1,12 @@ +#define __is_constexpr(x) \ + (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) + +static void test(int x) { + static int b[] = { + [__builtin_choose_expr(__is_constexpr(1 << 1), 1, x)] = 0, + }; +} + +/* + * check-name: constexpr-shift + */ diff --git a/usr/src/tools/smatch/src/validation/constexpr-string.c b/usr/src/tools/smatch/src/validation/constexpr-string.c index e641a83eb2..1db101eebc 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-string.c +++ b/usr/src/tools/smatch/src/validation/constexpr-string.c @@ -1,7 +1,7 @@ static char *a = "foobar"; // OK /* - * check-name: string literal constness verification. + * check-name: constness of string literal * check-command: sparse -Wconstexpr-not-const $file * * check-error-start diff --git a/usr/src/tools/smatch/src/validation/constexpr-types-compatible-p.c b/usr/src/tools/smatch/src/validation/constexpr-types-compatible-p.c index 1969bf3bd2..1179e9d606 100644 --- a/usr/src/tools/smatch/src/validation/constexpr-types-compatible-p.c +++ b/usr/src/tools/smatch/src/validation/constexpr-types-compatible-p.c @@ -1,7 +1,7 @@ static int a[] = {[__builtin_types_compatible_p(int, int)] = 0}; /* - * check-name: __builtin_types_compatible_p() constness verification. + * check-name: constness of __builtin_types_compatible_p() * * check-error-start * check-error-end diff --git a/usr/src/tools/smatch/src/validation/context-stmt.c b/usr/src/tools/smatch/src/validation/context-stmt.c new file mode 100644 index 0000000000..2884a8a213 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/context-stmt.c @@ -0,0 +1,62 @@ +static void foo(int x) +{ + __context__(0); // OK + __context__(x, 0); // OK + __context__ (x, 1); // OK + + __context__(x); // KO: no const expr + __context__(1,x); // KO: no const expr + + __context__; // KO: no expression at all + __context__(; // KO: no expression at all + + __context__ 0; // KO: need parens + __context__ x, 0; // KO: need parens + __context__(x, 0; // KO: unmatched parens + __context__ x, 0); // KO: unmatched parens + __context__(0; // KO: unmatched parens + __context__ 0); // KO: unmatched parens + + __context__(); // KO: no expression at all + __context__(,0); // KO: no expression at all + __context__(x,); // KO: no expression at all + __context__(,); // KO: no expression at all +} + +/* + * check-name: context-stmt + * check-command: sparse -Wno-context $file + * + * check-error-start +context-stmt.c:10:20: error: Expected ( after __context__ statement +context-stmt.c:10:20: error: got ; +context-stmt.c:11:21: error: expression expected after '(' +context-stmt.c:11:21: error: got ; +context-stmt.c:11:21: error: Expected ) at end of __context__ statement +context-stmt.c:11:21: error: got ; +context-stmt.c:13:21: error: Expected ( after __context__ statement +context-stmt.c:13:21: error: got 0 +context-stmt.c:14:21: error: Expected ( after __context__ statement +context-stmt.c:14:21: error: got x +context-stmt.c:15:25: error: Expected ) at end of __context__ statement +context-stmt.c:15:25: error: got ; +context-stmt.c:16:21: error: Expected ( after __context__ statement +context-stmt.c:16:21: error: got x +context-stmt.c:17:22: error: Expected ) at end of __context__ statement +context-stmt.c:17:22: error: got ; +context-stmt.c:18:21: error: Expected ( after __context__ statement +context-stmt.c:18:21: error: got 0 +context-stmt.c:20:21: error: expression expected after '(' +context-stmt.c:20:21: error: got ) +context-stmt.c:21:21: error: expression expected after '(' +context-stmt.c:21:21: error: got , +context-stmt.c:22:23: error: expression expected after ',' +context-stmt.c:22:23: error: got ) +context-stmt.c:23:21: error: expression expected after '(' +context-stmt.c:23:21: error: got , +context-stmt.c:23:22: error: expression expected after ',' +context-stmt.c:23:22: error: got ) +context-stmt.c:7:21: error: bad constant expression +context-stmt.c:8:23: error: bad constant expression + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/crash-select.c b/usr/src/tools/smatch/src/validation/crash-select.c new file mode 100644 index 0000000000..cec00baf88 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/crash-select.c @@ -0,0 +1,18 @@ +struct s { + void *b; + long c; +}; + +long d(void); +static long f(void) +{ + struct s s; + s.c = d(); + if (s.c) + s.c = 2; + return s.c; +} + +/* + * check-name: crash-select + */ diff --git a/usr/src/tools/smatch/src/validation/doc/cdoc.cdoc b/usr/src/tools/smatch/src/validation/doc/cdoc.cdoc new file mode 100644 index 0000000000..f2d99ab9ea --- /dev/null +++ b/usr/src/tools/smatch/src/validation/doc/cdoc.cdoc @@ -0,0 +1,177 @@ +/// +// Title +// ----- + +/// +// short description +int a(int param, int arg); + +/// +// short description +// longer description +int b(int param, int arg); + +/// +// short description +// +// longer description with empty line +int c(int param, int arg); + +/// +// short description +// longer description +// which needs two lines +int d(int param, int arg); + +/// +// short description +// +// longer description with empty line +// which needs two lines +int e(int param, int arg); + +/// +// condensed format +// @param: desc param +// @arg: desc arg +// @return: desc return +// longer description +int f(int param, int arg); + +/// +// more airy format +// +// @param: desc param +// @arg: desc arg +// @return: desc return +// +// longer description +int g(int param, int arg); + +/// +// short description +// @return: ``1`` if @param is zero, +// ``0`` otherwise. +int h(int param, int arg); + +/// +// short description +// @return: +// * ``1`` if @param is zero, +// * ``0`` otherwise. +int i(int param, int arg); + +/// +// short description +int m(int param, int arg) +{ return 0; } + +/// +// short description +int n(int param, + int arg) +{ return 0; } + +/// +// short description +int o(int param, int arg); + +/// +// short description +int p(int param, + int arg); + + +/* + * check-name: cdoc + * check-command: Documentation/sphinx/cdoc.py < $file + * + * check-output-start + 2: Title + 3: ----- + 4: + 4: + 5: + 7: .. c:function:: int a(int param, int arg) + 8: + 6: Short description. + 7: + 12: .. c:function:: int b(int param, int arg) + 13: + 10: Short description. + 11: + 11: longer description + 12: + 18: .. c:function:: int c(int param, int arg) + 19: + 15: Short description. + 16: + 17: longer description with empty line + 18: + 24: .. c:function:: int d(int param, int arg) + 25: + 21: Short description. + 22: + 22: longer description + 23: which needs two lines + 24: + 31: .. c:function:: int e(int param, int arg) + 32: + 27: Short description. + 28: + 29: longer description with empty line + 30: which needs two lines + 31: + 39: .. c:function:: int f(int param, int arg) + 40: + 34: Condensed format. + 35: + 35: :param param: desc param + 36: :param arg: desc arg + 37: :return: desc return + 38: + 38: longer description + 39: + 49: .. c:function:: int g(int param, int arg) + 50: + 42: More airy format. + 43: + 44: :param param: desc param + 45: :param arg: desc arg + 46: :return: desc return + 47: + 48: longer description + 49: + 55: .. c:function:: int h(int param, int arg) + 56: + 52: Short description. + 53: + 53: :return: ``1`` if **param** is zero, + 54: ``0`` otherwise. + 54: + 62: .. c:function:: int i(int param, int arg) + 63: + 58: Short description. + 59: + 59: :return: + 60: * ``1`` if **param** is zero, + 61: * ``0`` otherwise. + 60: + 66: .. c:function:: int m(int param, int arg) + 67: + 65: Short description. + 66: + 71: .. c:function:: int n(int param, int arg) + 72: + 70: Short description. + 71: + 77: .. c:function:: int o(int param, int arg) + 78: + 76: Short description. + 77: + 81: .. c:function:: int p(int param, int arg) + 82: + 80: Short description. + 81: + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/empty-expr.c b/usr/src/tools/smatch/src/validation/empty-expr.c new file mode 100644 index 0000000000..61decf7370 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/empty-expr.c @@ -0,0 +1,26 @@ +static int foo(void) +{ + switch () { + case 0: + return 0; + default: + return 1; + } +} + +static int bar(void) +{ + if () + return 0; + else + return 1; +} + +/* + * check-name: empty expression + * + * check-error-start +empty-expr.c:3:17: error: an expression is expected before ')' +empty-expr.c:13:13: error: an expression is expected before ')' + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/enum+mode.c b/usr/src/tools/smatch/src/validation/enum+mode.c new file mode 100644 index 0000000000..182af1895d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum+mode.c @@ -0,0 +1,18 @@ +enum e { ZERO, ONE, TWO }; + +struct s { + enum e __attribute__ ((mode(__byte__))) b; + enum e __attribute__ ((mode(__word__))) w; + enum e __attribute__ ((mode(__TI__))) t; +}; + +static struct s s; + +_Static_assert(sizeof(s.b) == 1, ""); +_Static_assert(sizeof(s.w) == sizeof(long), ""); +_Static_assert(sizeof(s.t) == sizeof(long long), ""); + +/* + * check-name: enum+mode + * check-known-to-fail + */ diff --git a/usr/src/tools/smatch/src/validation/enum-base-type.c b/usr/src/tools/smatch/src/validation/enum-base-type.c new file mode 100644 index 0000000000..cae801b159 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum-base-type.c @@ -0,0 +1,29 @@ +enum n { + NA, + NB = 1L, + NC = 1UL, + ND = 1LL, + NE = 1ULL, + NF = -1, + NG = -1L, + NH = -1LL, +}; +_Static_assert(sizeof(enum n) == sizeof(int), "+-1"); + +enum m { + MA = 0L, + MB = 1L, + MG = -1L, +}; +_Static_assert(sizeof(enum m) == sizeof(int), "+-1L"); + +enum p { + PA = 0UL, + PB = 1UL, +}; +_Static_assert(sizeof(enum p) == sizeof(int), "UL"); + +/* + * check-name: enum-base-type + * check-command: sparse -m64 $file + */ diff --git a/usr/src/tools/smatch/src/validation/enum-bitwise-bad.c b/usr/src/tools/smatch/src/validation/enum-bitwise-bad.c new file mode 100644 index 0000000000..6d31ca38c0 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum-bitwise-bad.c @@ -0,0 +1,20 @@ +#define __bitwise __attribute__((bitwise)) +#define __force __attribute__((force)) + +typedef int __bitwise apple_t; +typedef int __bitwise orange_t; + +enum fruit { + A = (__force apple_t) 0, + B = (__force orange_t) 1, +}; + +/* + * check-name: enum-bitwise-bad + * + * check-error-start +enum-bitwise-bad.c:9:14: error: incompatible restricted type +enum-bitwise-bad.c:9:14: expected: restricted apple_t +enum-bitwise-bad.c:9:14: got: restricted orange_t + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/enum-bitwise-mixed.c b/usr/src/tools/smatch/src/validation/enum-bitwise-mixed.c new file mode 100644 index 0000000000..07d77176c6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum-bitwise-mixed.c @@ -0,0 +1,29 @@ +#define __bitwise __attribute__((bitwise)) +#define __force __attribute__((force)) + +typedef long long __bitwise bits; + +enum a { + AR = (__force bits) 0, + AP = 0, + AS = (__force bits) 1, + AQ = 1, +}; +_Static_assert(sizeof(AP) == sizeof(int), "is bad?"); + +enum b { + BP = 0, + BR = (__force bits) 0, + BQ = 1, + BS = (__force bits) 1, +}; +_Static_assert(sizeof(BP) == sizeof(int), "is bad?"); + +/* + * check-name: enum-bitwise-mixed + * + * check-error-start +enum-bitwise-mixed.c:8:14: warning: mixed bitwiseness +enum-bitwise-mixed.c:16:15: warning: mixed bitwiseness + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/enum-bitwise.c b/usr/src/tools/smatch/src/validation/enum-bitwise.c new file mode 100644 index 0000000000..fcdb8d7a06 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum-bitwise.c @@ -0,0 +1,19 @@ +#define __bitwise __attribute__((bitwise)) +#define __force __attribute__((force)) + +typedef long long __bitwise bits; + +enum r { + RZ = (__force bits) 0, + RO = (__force bits) 1, + RM = (__force bits) -1, +}; + +_Static_assert([typeof(RZ)] == [bits], "RZ"); +_Static_assert([typeof(RO)] == [bits], "RO"); +_Static_assert([typeof(RM)] == [bits], "RM"); +_Static_assert(sizeof(enum r) == sizeof(bits), "bits"); + +/* + * check-name: enum-bitwise + */ diff --git a/usr/src/tools/smatch/src/validation/enum-bounds.c b/usr/src/tools/smatch/src/validation/enum-bounds.c new file mode 100644 index 0000000000..49f7a1ed81 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum-bounds.c @@ -0,0 +1,25 @@ +enum bound_int_max { + IMAX = __INT_MAX__, +}; +_Static_assert([typeof(IMAX)] == [int], ""); + +enum bound_int_maxp1 { + IMP1 = __INT_MAX__ + 1L, +}; +_Static_assert([typeof(IMP1)] == [unsigned int], ""); + +enum bound_int_maxm1 { + IMM1 = -__INT_MAX__ - 1L, +}; +_Static_assert([typeof(IMM1)] == [int], ""); + +enum bound_int_maxm2 { + IMM2 = -__INT_MAX__ - 2L, +}; +_Static_assert([typeof(IMM2)] == [long], ""); + +/* + * check-name: enum-bounds + * check-command: sparse -m64 $file + * check-assert: sizeof(long) == 8 + */ diff --git a/usr/src/tools/smatch/src/validation/enum-init-constness.c b/usr/src/tools/smatch/src/validation/enum-init-constness.c new file mode 100644 index 0000000000..5b95bc06a6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum-init-constness.c @@ -0,0 +1,9 @@ +extern int invalid; + +enum e { + E = 1 ? 1 : invalid +}; + +/* + * check-name: enum-init-constness + */ diff --git a/usr/src/tools/smatch/src/validation/enum-invalid.c b/usr/src/tools/smatch/src/validation/enum-invalid.c new file mode 100644 index 0000000000..62f8864d4c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum-invalid.c @@ -0,0 +1,11 @@ +enum e { }; +enum f { F = 0.1 }; + +/* + * check-name: enum-invalid + * + * check-error-start +enum-invalid.c:1:10: error: empty enum definition +enum-invalid.c:2:14: error: bad constant expression + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/enum-min-size.c b/usr/src/tools/smatch/src/validation/enum-min-size.c new file mode 100644 index 0000000000..e8bd9fb1b5 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum-min-size.c @@ -0,0 +1,29 @@ +enum i { I = 1 }; +_Static_assert(sizeof(enum i) == sizeof(int), "int"); +enum u { U = 1U }; +_Static_assert(sizeof(enum u) == sizeof(int), "uint"); + +enum l { L = 1L }; +_Static_assert(sizeof(enum l) == sizeof(int), "long"); +enum m { M = 1UL }; +_Static_assert(sizeof(enum m) == sizeof(int), "ulong"); + +enum n { N = 1LL }; +_Static_assert(sizeof(enum n) == sizeof(int), "llong"); +enum o { O = 1ULL }; +_Static_assert(sizeof(enum o) == sizeof(int), "ullong"); + + +enum mi { MI = -1 }; +_Static_assert(sizeof(enum i) == sizeof(int), "int"); + +enum ml { ML = -1L }; +_Static_assert(sizeof(enum l) == sizeof(int), "long"); + +enum mn { MN = -1LL }; +_Static_assert(sizeof(enum n) == sizeof(int), "llong"); + + +/* + * check-name: enum-min-size + */ diff --git a/usr/src/tools/smatch/src/validation/enum-mismatch.c b/usr/src/tools/smatch/src/validation/enum-mismatch.c index 9a929d24ca..1bdb1d6c2f 100644 --- a/usr/src/tools/smatch/src/validation/enum-mismatch.c +++ b/usr/src/tools/smatch/src/validation/enum-mismatch.c @@ -13,7 +13,7 @@ static enum eb foo(enum ea a) * * check-error-start enum-mismatch.c:7:16: warning: mixing different enum types -enum-mismatch.c:7:16: int enum ea versus -enum-mismatch.c:7:16: int enum eb +enum-mismatch.c:7:16: unsigned int enum ea versus +enum-mismatch.c:7:16: unsigned int enum eb * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/enum-same-type.c b/usr/src/tools/smatch/src/validation/enum-same-type.c new file mode 100644 index 0000000000..0ff49cb452 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum-same-type.c @@ -0,0 +1,14 @@ +enum num { + NEG = -1, + NIL = 0, + ONE = 1U, + DUO = 2LL, +}; + +_Static_assert([typeof(NIL)] == [typeof(NEG)], "enum same type"); +_Static_assert([typeof(ONE)] == [typeof(NEG)], "enum same type"); +_Static_assert([typeof(DUO)] == [typeof(NEG)], "enum same type"); + +/* + * check-name: enum-same-type + */ diff --git a/usr/src/tools/smatch/src/validation/enum-sign-gcc.c b/usr/src/tools/smatch/src/validation/enum-sign-gcc.c new file mode 100644 index 0000000000..03fa359079 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum-sign-gcc.c @@ -0,0 +1,64 @@ +// For enum's underlying/compatible type: +// std C: unspecified +// GCC: 'unsigned int' if no negative values, +// otherwise 'int' (see GCC manul 4.9). +// But also accept ulong, long +// For the type of the enumerators: +// std C: 'int' +// GCC: 'int' if the value fit in a 'int' +// otherwise same as the enum underlying type? +// +// The following tests match GCC's choices + +#define is_unsigned(X) ((typeof(X))-1 > 0) + +enum u { + U = 1U, // fit in 'int' + // no negatives +}; +_Static_assert(sizeof(enum u) == sizeof(int), "size"); +_Static_assert(is_unsigned(enum u), "enum u"); +_Static_assert(is_unsigned(U) == 0, "value U"); // fail + +enum v { + V = __INT_MAX__ + 1U, // doesn't fit in 'int' + // no negatives +}; +_Static_assert(sizeof(enum v) == sizeof(int), "size"); +_Static_assert(is_unsigned(enum v), "enum v"); +_Static_assert(is_unsigned(V) == 1, "value V"); + +enum w { + W = __LONG_MAX__ + 1UL, // doesn't fit in 'long' +}; +_Static_assert(sizeof(enum w) == sizeof(long), "size"); +_Static_assert(is_unsigned(enum w), "enum w"); +_Static_assert(is_unsigned(W) == 1, "value W"); + +enum x { + A = 1, // fit in 'int' + B = 0x100000000UL, // doesn't fit in int +}; +_Static_assert(sizeof(enum x) == sizeof(long), "size"); +_Static_assert(is_unsigned(enum x), "enum x"); +_Static_assert(sizeof(A) == sizeof(int), "size A"); // fail +_Static_assert(is_unsigned(A) == 0, "enum A"); // fail +_Static_assert(sizeof(B) == sizeof(long), "size B"); +_Static_assert(is_unsigned(B) == 1, "enum B"); + +enum y { + C = 1, // fit in 'int' + D = 0x100000000L, // doesn't fit in int +}; +_Static_assert(sizeof(enum y) == sizeof(long), "size"); +_Static_assert(is_unsigned(enum y), "enum y"); +_Static_assert(sizeof(C) == sizeof(int), "size C"); // fail +_Static_assert(is_unsigned(C) == 0, "enum C"); // fail +_Static_assert(sizeof(D) == sizeof(long), "size D"); +_Static_assert(is_unsigned(D) == 1, "enum D"); + +/* + * check-name: enum-sign-gcc + * check-command: sparse -m64 $file + * check-assert: sizeof(long) == 8 + */ diff --git a/usr/src/tools/smatch/src/validation/enum-typecheck.c b/usr/src/tools/smatch/src/validation/enum-typecheck.c new file mode 100644 index 0000000000..77b77b47b2 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/enum-typecheck.c @@ -0,0 +1,39 @@ +enum good { G, }; +enum bad { B, }; +enum good g; + +enum good compat_int(void) { return 1; } + +void parg(enum good); +void parg(enum bad); + +void farg(enum good a); +void farg(enum bad a) { } + +enum good pret(void); +enum bad pret(void); + +enum good fret(void); +enum bad fret(void) { return 0; } + + +enum good *ptr; +enum bad *ptr; + +enum good *gptr = &g; +enum bad *bptr = &g; + +/* + * check-name: enum-typecheck + * check-command: sparse -Wno-decl $file + * check-known-to-fail + * + * check-error-start +enum-typecheck.c:8:6: error: symbol 'parg' redeclared with different type +enum-typecheck.c:11:6: error: symbol 'farg' redeclared with different type +enum-typecheck.c:14:11: error: symbol 'pret' redeclared with different type +enum-typecheck.c:17:11: error: symbol 'fret' redeclared with different type +enum-typecheck.c:21:12: error: symbol 'ptr' redeclared with different type +enum-typecheck.c:24:20: warning: incorrect type in initializer (different type sizes) + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/error-at-eof.c b/usr/src/tools/smatch/src/validation/error-at-eof.c new file mode 100644 index 0000000000..7d93365111 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/error-at-eof.c @@ -0,0 +1,10 @@ +/* + * check-name: error-at-eof + * + * check-error-start +error-at-eof.c:11:0: error: Expected ; at end of declaration +error-at-eof.c:11:0: error: got end-of-input + * check-error-end + */ + +int a diff --git a/usr/src/tools/smatch/src/validation/eval-typeof-vla.c b/usr/src/tools/smatch/src/validation/eval-typeof-vla.c new file mode 100644 index 0000000000..56a9ec2019 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/eval-typeof-vla.c @@ -0,0 +1,26 @@ +extern int a[1]; + +static int foo(int n) +{ + int i = 0; + int (*p)[1] = (typeof(++i, (int (*)[n])a)) &a; + + (void) p; + + return i; +} + +/* + * check-name: eval-typeof-vla + * check-command: test-linearize -Wno-vla $file + * check-known-to-fail + * + * check-output-start +foo: +.L0: + <entry-point> + ret.32 $1 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/expand/bad-shift.c b/usr/src/tools/smatch/src/validation/expand/bad-shift.c new file mode 100644 index 0000000000..22c4341f16 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/expand/bad-shift.c @@ -0,0 +1,64 @@ +#define MAX (sizeof(int) * __CHAR_BIT__) + +static int lmax(int a) +{ + return 1 << MAX; +} + +static int lneg(int a) +{ + return 1 << -1; +} + +static int rmax(int a) +{ + return 1 >> MAX; +} + +static int rneg(int a) +{ + return 1 >> -1; +} + +/* + * check-name: bad-shift + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +lmax: +.L0: + <entry-point> + shl.32 %r1 <- $1, $32 + ret.32 %r1 + + +lneg: +.L2: + <entry-point> + shl.32 %r3 <- $1, $0xffffffff + ret.32 %r3 + + +rmax: +.L4: + <entry-point> + asr.32 %r5 <- $1, $32 + ret.32 %r5 + + +rneg: +.L6: + <entry-point> + asr.32 %r7 <- $1, $0xffffffff + ret.32 %r7 + + + * check-output-end + * + * check-error-start +expand/bad-shift.c:5:18: warning: shift too big (32) for type int +expand/bad-shift.c:10:18: warning: shift count is negative (-1) +expand/bad-shift.c:15:18: warning: shift too big (32) for type int +expand/bad-shift.c:20:18: warning: shift count is negative (-1) + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/expand/builtin-expect.c b/usr/src/tools/smatch/src/validation/expand/builtin-expect.c new file mode 100644 index 0000000000..1b0c7c180f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/expand/builtin-expect.c @@ -0,0 +1,100 @@ +int flia(long a) +{ + return __builtin_expect(a, 1); +} + +int flic(void) +{ + return __builtin_expect(1L << 32 | 1, 1); +} + +long fila(int a) +{ + return __builtin_expect(a, 1); +} + +long filc(void) +{ + return __builtin_expect(1L << 32 | 1, 1); +} + +long filu(void) +{ + return __builtin_expect(0x80000000U, 1); +} + +long fils(void) +{ + return __builtin_expect((int)0x80000000, 1); +} + +void *fptr(void *a) +{ + return __builtin_expect(a, a); +} + +/* + * check-name: builtin-expect + * check-command: test-linearize -m64 -Wno-decl $file + * check-assert: sizeof(long) == 8 + * + * check-output-start +flia: +.L0: + <entry-point> + trunc.32 %r2 <- (64) %arg1 + ret.32 %r2 + + +flic: +.L2: + <entry-point> + ret.32 $1 + + +fila: +.L4: + <entry-point> + sext.64 %r6 <- (32) %arg1 + ret.64 %r6 + + +filc: +.L6: + <entry-point> + ret.64 $0x100000001 + + +filu: +.L8: + <entry-point> + ret.64 $0x80000000 + + +fils: +.L10: + <entry-point> + ret.64 $0xffffffff80000000 + + +fptr: +.L12: + <entry-point> + ret.64 %arg1 + + + * check-output-end + * + * check-error-start +expand/builtin-expect.c:33:33: warning: incorrect type in argument 1 (different base types) +expand/builtin-expect.c:33:33: expected long +expand/builtin-expect.c:33:33: got void *a +expand/builtin-expect.c:33:36: warning: incorrect type in argument 2 (different base types) +expand/builtin-expect.c:33:36: expected long +expand/builtin-expect.c:33:36: got void *a +expand/builtin-expect.c:33:32: warning: incorrect type in return expression (different base types) +expand/builtin-expect.c:33:32: expected void * +expand/builtin-expect.c:33:32: got long +expand/builtin-expect.c:8:42: warning: cast truncates bits from constant value (100000001 becomes 1) + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/expand/builtin_fpclassify.c b/usr/src/tools/smatch/src/validation/expand/builtin_fpclassify.c new file mode 100644 index 0000000000..506927dd36 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/expand/builtin_fpclassify.c @@ -0,0 +1,26 @@ +enum { FP_NAN, FP_INF, FP_NOR, FP_SUB, FP_ZERO }; + +#define classify(X) __builtin_fpclassify(FP_NAN,FP_INF,FP_NOR,FP_SUB,FP_ZERO,X) + +int test(void) +{ + if (classify(__builtin_nan("0")) != FP_NAN) + return 0; + if (classify(__builtin_inf("0")) != FP_INF) + return 0; + if (classify(1.0) != FP_NOR) + return 0; + if (classify(0.0) != FP_ZERO) + return 0; + + return 1; +} + +/* + * check-name: builtin_fpclassify + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: ret\\..*\\$1 + */ diff --git a/usr/src/tools/smatch/src/validation/expand/builtin_huge_val.c b/usr/src/tools/smatch/src/validation/expand/builtin_huge_val.c new file mode 100644 index 0000000000..09ef2a6f34 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/expand/builtin_huge_val.c @@ -0,0 +1,39 @@ +static float huge_valf(void) +{ + return __builtin_huge_valf(); +} + +static double huge_val(void) +{ + return __builtin_huge_val(); +} + +static long double huge_vall(void) +{ + return __builtin_huge_vall(); +} + + +static float inff(void) +{ + return __builtin_inff(); +} + +static double inf(void) +{ + return __builtin_inf(); +} + +static long double infl(void) +{ + return __builtin_infl(); +} + +/* + * check-name: builtin_huge_val expand + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-excludes: call + */ diff --git a/usr/src/tools/smatch/src/validation/expand/builtin_isinf.c b/usr/src/tools/smatch/src/validation/expand/builtin_isinf.c new file mode 100644 index 0000000000..36b20a60e2 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/expand/builtin_isinf.c @@ -0,0 +1,20 @@ +int test(void) +{ + if (!__builtin_isinf(__builtin_inff())) + return 0; + if (!__builtin_isinf(__builtin_inf())) + return 0; + if (!__builtin_isinf(__builtin_infl())) + return 0; + + return 1; +} + +/* + * check-name: builtin_isinf expand + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: ret\\..*\\$1 + */ diff --git a/usr/src/tools/smatch/src/validation/expand/builtin_isnan.c b/usr/src/tools/smatch/src/validation/expand/builtin_isnan.c new file mode 100644 index 0000000000..07c7e5e545 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/expand/builtin_isnan.c @@ -0,0 +1,20 @@ +int test(void) +{ + if (!__builtin_isnan(__builtin_nanf("0"))) + return 0; + if (!__builtin_isnan(__builtin_nan("0"))) + return 0; + if (!__builtin_isnan(__builtin_nanl("0"))) + return 0; + + return 1; +} + +/* + * check-name: builtin_isnan expand + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: ret\\..*\\$1 + */ diff --git a/usr/src/tools/smatch/src/validation/expand/builtin_isnormal.c b/usr/src/tools/smatch/src/validation/expand/builtin_isnormal.c new file mode 100644 index 0000000000..0b69e27317 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/expand/builtin_isnormal.c @@ -0,0 +1,20 @@ +int test(void) +{ + if (!__builtin_isnormal(1.0F)) + return 0; + if (!__builtin_isnormal(1.0)) + return 0; + if (!__builtin_isnormal(1.0L)) + return 0; + + return 1; +} + +/* + * check-name: builtin_isnormal expand + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: ret\\..*\\$1 + */ diff --git a/usr/src/tools/smatch/src/validation/expand/builtin_nan.c b/usr/src/tools/smatch/src/validation/expand/builtin_nan.c new file mode 100644 index 0000000000..20e3ae925c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/expand/builtin_nan.c @@ -0,0 +1,23 @@ +static float nanf(void) +{ + return __builtin_nanf("0"); +} + +static double nan(void) +{ + return __builtin_nan("0"); +} + +static long double nanl(void) +{ + return __builtin_nanl("0"); +} + +/* + * check-name: builtin_nan expand + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-excludes: call + */ diff --git a/usr/src/tools/smatch/src/validation/expand/function-pointer.c b/usr/src/tools/smatch/src/validation/expand/function-pointer.c new file mode 100644 index 0000000000..706ef9a443 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/expand/function-pointer.c @@ -0,0 +1,22 @@ +struct s { + int (*fun)(void); +}; + +inline struct s *inl(struct s *p) +{ + 1 + 0; + return p; +} + +static void tst(struct s *s) +{ + inl(s)->fun(); +} + +/* + * check-name: function-pointer + * check-command: test-linearize -fdump-ir $file + * + * check-output-ignore + * check-output-excludes: add\\.32.*\\$1, \\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/external-function-has-definition.c b/usr/src/tools/smatch/src/validation/external-function-has-definition.c deleted file mode 100644 index cd96df4ad1..0000000000 --- a/usr/src/tools/smatch/src/validation/external-function-has-definition.c +++ /dev/null @@ -1,15 +0,0 @@ - -extern void myfunction(void); - -extern void -myfunction(void) -{ - return; -} - -/* - * check-name: -Wno-external-function-has-definition works - * check-command: sparse -Wno-external-function-has-definition - * check-error-start - * check-error-end - */ diff --git a/usr/src/tools/smatch/src/validation/fdiag-prefix.c b/usr/src/tools/smatch/src/validation/fdiag-prefix.c new file mode 100644 index 0000000000..71160d4560 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/fdiag-prefix.c @@ -0,0 +1,11 @@ +int a. + +/* + * check-name: fdiag-prefix + * check-command: sparse -fdiagnostic-prefix=prefix $file + * + * check-error-start +fdiag-prefix.c:1:6: prefix: error: Expected ; at end of declaration +fdiag-prefix.c:1:6: prefix: error: got . + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/fp-ops.c b/usr/src/tools/smatch/src/validation/fp-ops.c new file mode 100644 index 0000000000..96c246f845 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/fp-ops.c @@ -0,0 +1,57 @@ +double fadd(double x, double y) { return x + y; } +double fsub(double x, double y) { return x - y; } +double fmul(double x, double y) { return x * y; } +double fdiv(double x, double y) { return x / y; } +double fneg(double x) { return -x; } +_Bool ftst(double x) { return !x; } + +/* + * check-name: floating-point ops + * check-command: test-linearize -Wno-decl $file + + * check-output-start +fadd: +.L0: + <entry-point> + fadd.64 %r3 <- %arg1, %arg2 + ret.64 %r3 + + +fsub: +.L2: + <entry-point> + fsub.64 %r7 <- %arg1, %arg2 + ret.64 %r7 + + +fmul: +.L4: + <entry-point> + fmul.64 %r11 <- %arg1, %arg2 + ret.64 %r11 + + +fdiv: +.L6: + <entry-point> + fdiv.64 %r15 <- %arg1, %arg2 + ret.64 %r15 + + +fneg: +.L8: + <entry-point> + fneg.64 %r18 <- %arg1 + ret.64 %r18 + + +ftst: +.L10: + <entry-point> + setfval.64 %r21 <- 0.000000e+00 + fcmpoeq.1 %r23 <- %arg1, %r21 + ret.1 %r23 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/function-pointer-type.c b/usr/src/tools/smatch/src/validation/function-pointer-type.c new file mode 100644 index 0000000000..ebc4007ba2 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/function-pointer-type.c @@ -0,0 +1,12 @@ +extern int fun(void); + +void fa(void) { int (*f)(void); f = &fun; } +void f0(void) { int (*f)(void); f = fun; } // C99,C11 6.3.2.1p4 +void f1(void) { int (*f)(void); f = *fun; } // C99,C11 6.5.3.2p4 +void f2(void) { int (*f)(void); f = **fun; } // C99,C11 6.5.3.2p4 +void f3(void) { int (*f)(void); f = ***fun; } // C99,C11 6.5.3.2p4 + +/* + * check-name: type of function pointers + * check-command: sparse -Wno-decl $file + */ diff --git a/usr/src/tools/smatch/src/validation/function-redecl2.c b/usr/src/tools/smatch/src/validation/function-redecl2.c new file mode 100644 index 0000000000..3435aa00cb --- /dev/null +++ b/usr/src/tools/smatch/src/validation/function-redecl2.c @@ -0,0 +1,31 @@ +extern void exit (int __status) __attribute__ ((__noreturn__)); + +int func0(int a) __attribute__ ((pure)); + +__attribute__ ((pure)) +int func0(int a) +{ + return 0; +} + +__attribute__ ((noreturn)) void func1(int a); + +void func1(int a) +{ + exit(0); +} + +void func2(int a) __attribute__ ((noreturn)); + +__attribute__ ((noreturn)) +void func2(int a) +{ + exit(0); +} + +/* + * check-name: function-redecl2 + * + * check-known-to-fail + * + */ diff --git a/usr/src/tools/smatch/src/validation/goto-reserved.c b/usr/src/tools/smatch/src/validation/goto-reserved.c new file mode 100644 index 0000000000..fbaf03e142 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/goto-reserved.c @@ -0,0 +1,12 @@ +static void foo(void) +{ + goto return; +} + +/* + * check-name: goto-reserved + * + * check-error-start +goto-reserved.c:3:14: error: Trying to use reserved word 'return' as identifier + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/implicit-KR-arg-type1.c b/usr/src/tools/smatch/src/validation/implicit-KR-arg-type1.c new file mode 100644 index 0000000000..fe199ef52c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/implicit-KR-arg-type1.c @@ -0,0 +1,16 @@ +int foo(a, b) + int a; +{ + if (b) + return a; +} + +/* + * check-name: implicit-KR-arg-type1 + * check-command: sparse -Wold-style-definition -Wimplicit-int $file + * + * check-error-start +implicit-KR-arg-type1.c:2:9: warning: non-ANSI definition of function 'foo' +implicit-KR-arg-type1.c:1:12: error: missing type declaration for parameter 'b' + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/inc-dec-float.c b/usr/src/tools/smatch/src/validation/inc-dec-float.c new file mode 100644 index 0000000000..3cac8f3ee3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/inc-dec-float.c @@ -0,0 +1,13 @@ +double fincpre(double a) { ++a; return a; } +double fdecpre(double a) { --a; return a; } +double fincpost(double a) { a++; return a; } +double fdecpost(double a) { a--; return a; } + +/* + * check-name: float inc & dec + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * + * check-output-excludes: \\$1$ + * check-output-excludes: \\$-1$ + */ diff --git a/usr/src/tools/smatch/src/validation/incomplete-struct.c b/usr/src/tools/smatch/src/validation/incomplete-struct.c new file mode 100644 index 0000000000..f9429f33a2 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/incomplete-struct.c @@ -0,0 +1,23 @@ +struct s; + +void foo(struct s s) +{ +} + +struct s bar(void) +{ + struct s s; + return s; +} + +/* + * check-name: incomplete struct + * check-command: sparse -Wno-decl $file + * check-known-to-fail + * + * check-error-start +incomplete-struct.c:3:19: error: parameter 's' has incomplete type +incomplete-struct.c:7:10: error: return type is incomplete +incomplete-struct.c:9:11: error: 's' has incompelete type + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/infinite-loop01.c b/usr/src/tools/smatch/src/validation/infinite-loop01.c new file mode 100644 index 0000000000..521cfb4d4e --- /dev/null +++ b/usr/src/tools/smatch/src/validation/infinite-loop01.c @@ -0,0 +1,54 @@ +void fnp(void) +{ + int a; + for (;;) + a += 1; +} + +void fnm(void) +{ + int a; + for (;;) + a -= 1; +} + +void fna(void) +{ + int a; + for (;;) + a &= 1; +} + +void fno(void) +{ + int a; + for (;;) + a |= 1; +} + +void fnx(void) +{ + int a; + for (;;) + a ^= 1; +} + +void fnl(void) +{ + int a; + for (;;) + a <<= 1; +} + +void fnr(void) +{ + int a; + for (;;) + a >>= 1; +} + +/* + * check-name: infinite loop 01 + * check-command: sparse -Wno-decl $file + * check-timeout: + */ diff --git a/usr/src/tools/smatch/src/validation/infinite-loop02.c b/usr/src/tools/smatch/src/validation/infinite-loop02.c index 7d0761d87a..22028473a0 100644 --- a/usr/src/tools/smatch/src/validation/infinite-loop02.c +++ b/usr/src/tools/smatch/src/validation/infinite-loop02.c @@ -8,4 +8,5 @@ void foo(void) /* * check-name: infinite loop 02 * check-command: sparse -Wno-decl $file + * check-timeout: */ diff --git a/usr/src/tools/smatch/src/validation/infinite-loop03.c b/usr/src/tools/smatch/src/validation/infinite-loop03.c index ac8a9519d0..7af877a83e 100644 --- a/usr/src/tools/smatch/src/validation/infinite-loop03.c +++ b/usr/src/tools/smatch/src/validation/infinite-loop03.c @@ -13,4 +13,5 @@ static void foo(int *buf) /* * check-name: infinite loop 03 * check-command: sparse -Wno-decl $file + * check-timeout: */ diff --git a/usr/src/tools/smatch/src/validation/infinite-loop04.c b/usr/src/tools/smatch/src/validation/infinite-loop04.c new file mode 100644 index 0000000000..77865095f3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/infinite-loop04.c @@ -0,0 +1,18 @@ +extern void use(char); + +static void foo(char *b) +{ + while (b) { + if (b++) + continue; + ++b; + use(*b); + &b; + } +} + +/* + * check-name: internal infinite loop (4) + * check-command: sparse $file + * check-timeout: + */ diff --git a/usr/src/tools/smatch/src/validation/int128.c b/usr/src/tools/smatch/src/validation/int128.c index 53d678e2ca..adc7334929 100644 --- a/usr/src/tools/smatch/src/validation/int128.c +++ b/usr/src/tools/smatch/src/validation/int128.c @@ -30,7 +30,7 @@ u64 foo(u64 a, u64 b, u64 c, u32 s) * check-output-ignore * * check-output-contains: ret\\..*\\$16 - * check-output-contains: mulu\\.128 + * check-output-contains: mul\\.128 * check-output-contains: add\\.128 * * check-error-start diff --git a/usr/src/tools/smatch/src/validation/integer-const-expr.c b/usr/src/tools/smatch/src/validation/integer-const-expr.c new file mode 100644 index 0000000000..f41aa806ab --- /dev/null +++ b/usr/src/tools/smatch/src/validation/integer-const-expr.c @@ -0,0 +1,85 @@ +extern void *malloc(unsigned long); + +static inline __attribute__((__const__)) unsigned squarec(unsigned n) +{ + return n*n; +} + +static inline unsigned square(unsigned n) +{ + return n*n; +} + +static inline unsigned long long bignum(void) +{ + return 1000000000000ULL; +} + +static inline __attribute__((__const__)) unsigned long long bignumc(void) +{ + return 1000000000000ULL; +} + +// test if x is an integer constant expression [C99,C11 6.6p6] +#define ICE_P(x) \ + (__builtin_types_compatible_p(typeof(0?((void*)((long)(x)*0l)):(int*)1),int*)) + +#define CHX_P(X) __builtin_choose_expr(ICE_P(X), 1, 0) +#define CST_P(X) __builtin_constant_p(ICE_P(X)) + +#define TEST(R, X) _Static_assert(ICE_P(X) == R, "ICE_P(" #X ") => " #R); \ + _Static_assert(ICE_P(ICE_P(X)), "ICE_P2(" #X ")"); \ + _Static_assert(CHX_P(X) == R, "CHX_P(" #X ") => " #R); \ + _Static_assert(CST_P(X) == 1, "CST_P(" #X ")") + +int main(int argc, char *argv[]) +{ + char fla[3]; + char vla[argc++]; + char **p, **q; + int x = 5, y = 8; + void *v; + + p = &argv[3]; + q = &argv[6]; + + TEST(1, 4); + TEST(1, sizeof(long)); + TEST(1, 5ull - 3u); + TEST(1, 3.2); + TEST(1, sizeof(fla)); + + TEST(0, square(2)); + TEST(0, square(argc)); + TEST(0, squarec(2)); + TEST(0, squarec(argc)); + TEST(0, 1+argc-argc); + TEST(0, 1+argc+argc+1-argc-argc); + TEST(0, bignum() - 1); + TEST(0, 0*bignum()); + TEST(0, 0*bignumc()); + TEST(0, sizeof(vla)); + TEST(0, p); + TEST(0, p < q); + TEST(0, p++); + TEST(0, main); + TEST(0, malloc(8)); + TEST(0, v = malloc(8)); + TEST(0, v); + TEST(0, x++); + TEST(0, y++); + TEST(0, (3, 2, 1)); + TEST(0, ({x++; 0; })); + TEST(0, ({square(y--); 0; })); + TEST(0, (square(x), 3)); + TEST(0, (squarec(x), 3)); + TEST(0, ({squarec(x); 3;})); + TEST(0, ({squarec(x);})); + + return 0; +} + +/* + * check-name: integer-const-expr + * check-command: sparse -Wno-vla $file + */ diff --git a/usr/src/tools/smatch/src/validation/kill-load.c b/usr/src/tools/smatch/src/validation/kill-load.c index 45fb83e4ba..563e724c4c 100644 --- a/usr/src/tools/smatch/src/validation/kill-load.c +++ b/usr/src/tools/smatch/src/validation/kill-load.c @@ -13,5 +13,5 @@ void ind(volatile int *p,int i) { int v = i++; if (i && 0) p[v]; } * - bb unreachable. * * check-output-ignore - * check-output-pattern-1-times: load\\. + * check-output-pattern(1): load\\. */ diff --git a/usr/src/tools/smatch/src/validation/kill-phi-ttsbb.c b/usr/src/tools/smatch/src/validation/kill-phi-ttsbb.c index 178a65d197..7fea30bfd6 100644 --- a/usr/src/tools/smatch/src/validation/kill-phi-ttsbb.c +++ b/usr/src/tools/smatch/src/validation/kill-phi-ttsbb.c @@ -1,7 +1,7 @@ int def(void); void use(int); -static int foo(int a, int b) +static void foo(int a, int b) { int c; diff --git a/usr/src/tools/smatch/src/validation/kill-store.c b/usr/src/tools/smatch/src/validation/kill-store.c index 8f72532266..204b036737 100644 --- a/usr/src/tools/smatch/src/validation/kill-store.c +++ b/usr/src/tools/smatch/src/validation/kill-store.c @@ -12,5 +12,5 @@ void dead(int *p, int i) { int v = i++; if (i && 0) p[v] = 0; } * - bb unreachable. * * check-output-ignore - * check-output-pattern-1-times: store\\. + * check-output-pattern(1): store\\. */ diff --git a/usr/src/tools/smatch/src/validation/kill-switch.c b/usr/src/tools/smatch/src/validation/kill-switch.c new file mode 100644 index 0000000000..1316a775ea --- /dev/null +++ b/usr/src/tools/smatch/src/validation/kill-switch.c @@ -0,0 +1,17 @@ +extern int i; + +static void foo(void) +{ + switch (i) { + case 0: + ; + } +} + +/* + * check-name: kill-switch + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-excludes: load\\. + */ diff --git a/usr/src/tools/smatch/src/validation/label-redefined.c b/usr/src/tools/smatch/src/validation/label-redefined.c new file mode 100644 index 0000000000..c98e815c1f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/label-redefined.c @@ -0,0 +1,17 @@ +extern void fun(void); + +static void foo(int p) +{ +l: + if (p) +l: + fun(); +} + +/* + * check-name: label-redefined + * + * check-error-start +label-redefined.c:7:1: error: label 'l' redefined + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/asm-toplevel.c b/usr/src/tools/smatch/src/validation/linear/asm-toplevel.c index 8bdd7fc125..8bdd7fc125 100644 --- a/usr/src/tools/smatch/src/validation/asm-toplevel.c +++ b/usr/src/tools/smatch/src/validation/linear/asm-toplevel.c diff --git a/usr/src/tools/smatch/src/validation/linear/bitfield-expand-deref.c b/usr/src/tools/smatch/src/validation/linear/bitfield-expand-deref.c new file mode 100644 index 0000000000..43e1078d5f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/bitfield-expand-deref.c @@ -0,0 +1,27 @@ +struct s { + int a:8; + int b:8; +}; + +int foo(void) +{ + struct s x = { .a = 12, .b = 34, }; + + return x.b; +} + +int bar(int a) +{ + struct s x = { .a = 12, .b = a, }; + + return x.b; +} + +/* + * check-name: bitfield expand deref + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: ret\\..*\\$12 + * check-output-contains: ret\\..*\\$34 + */ diff --git a/usr/src/tools/smatch/src/validation/linear/bitfield-inc.c b/usr/src/tools/smatch/src/validation/linear/bitfield-inc.c new file mode 100644 index 0000000000..56997592e0 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/bitfield-inc.c @@ -0,0 +1,16 @@ +struct s { + int f:5; +}; + +void inc(struct s *p) +{ + p->f++; +} + +/* + * check-name: bitfield-inc + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: add\\.5 + */ diff --git a/usr/src/tools/smatch/src/validation/linear/bitfield-init-mask.c b/usr/src/tools/smatch/src/validation/linear/bitfield-init-mask.c index 94afa400c8..aac21e6140 100644 --- a/usr/src/tools/smatch/src/validation/linear/bitfield-init-mask.c +++ b/usr/src/tools/smatch/src/validation/linear/bitfield-init-mask.c @@ -18,7 +18,7 @@ struct bfu bfu_init_20_23(int a) /* * check-name: bitfield initializer mask - * check-command: test-linearize -fdump-linearize=only -Wno-decl $file + * check-command: test-linearize -fdump-ir=linearize -Wno-decl $file * check-output-ignore * * check-output-contains: and\\..*fffff800\$ diff --git a/usr/src/tools/smatch/src/validation/linear/bitfield-preinc.c b/usr/src/tools/smatch/src/validation/linear/bitfield-preinc.c new file mode 100644 index 0000000000..783327ae8a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/bitfield-preinc.c @@ -0,0 +1,18 @@ +struct s { + int f:3; +}; + +int preinc(void) +{ + struct s s = { 7 }; + return ++s.f; +} + +/* + * check-name: bitfield-preinc + * check-description: ++X is equivalent to X+=1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret.32 *\\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/linear/bitfield-size.c b/usr/src/tools/smatch/src/validation/linear/bitfield-size.c new file mode 100644 index 0000000000..dcda930dc5 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/bitfield-size.c @@ -0,0 +1,183 @@ +struct u { + unsigned int f:3; +}; + +unsigned int upostinc(struct u *x) +{ + return x->f++; +} + +unsigned int upreinc(struct u *x) +{ + return ++x->f; +} + +void ucpy(struct u *d, const struct u *s) +{ + d->f = s->f; +} + + +struct s { + int f:3; +}; + +int spostinc(struct s *x) +{ + return x->f++; +} + +int spreinc(struct s *x) +{ + return ++x->f; +} + +void scpy(struct s *d, const struct s *s) +{ + d->f = s->f; +} + +/* + * check-name: bitfield-size + * check-command: test-linearize -m64 -Wno-decl -fdump-ir $file + * check-assert: sizeof(void *) == 8 + * + * check-output-start +upostinc: +.L0: + <entry-point> + store.64 %arg1 -> 0[x] + load.64 %r1 <- 0[x] + load.32 %r2 <- 0[%r1] + trunc.3 %r3 <- (32) %r2 + zext.32 %r4 <- (3) %r3 + add.32 %r5 <- %r4, $1 + trunc.3 %r6 <- (32) %r5 + load.32 %r7 <- 0[%r1] + zext.32 %r8 <- (3) %r6 + and.32 %r9 <- %r7, $0xfffffff8 + or.32 %r10 <- %r9, %r8 + store.32 %r10 -> 0[%r1] + zext.32 %r11 <- (3) %r4 + phisrc.32 %phi1(return) <- %r11 + br .L1 + +.L1: + phi.32 %r12 <- %phi1(return) + ret.32 %r12 + + +upreinc: +.L2: + <entry-point> + store.64 %arg1 -> 0[x] + load.64 %r13 <- 0[x] + load.32 %r14 <- 0[%r13] + trunc.3 %r15 <- (32) %r14 + zext.32 %r16 <- (3) %r15 + add.32 %r17 <- %r16, $1 + trunc.3 %r18 <- (32) %r17 + load.32 %r19 <- 0[%r13] + zext.32 %r20 <- (3) %r18 + and.32 %r21 <- %r19, $0xfffffff8 + or.32 %r22 <- %r21, %r20 + store.32 %r22 -> 0[%r13] + zext.32 %r23 <- (3) %r18 + phisrc.32 %phi2(return) <- %r23 + br .L3 + +.L3: + phi.32 %r24 <- %phi2(return) + ret.32 %r24 + + +ucpy: +.L4: + <entry-point> + store.64 %arg1 -> 0[d] + store.64 %arg2 -> 0[s] + load.64 %r25 <- 0[s] + load.32 %r26 <- 0[%r25] + trunc.3 %r27 <- (32) %r26 + load.64 %r28 <- 0[d] + load.32 %r29 <- 0[%r28] + zext.32 %r30 <- (3) %r27 + and.32 %r31 <- %r29, $0xfffffff8 + or.32 %r32 <- %r31, %r30 + store.32 %r32 -> 0[%r28] + br .L5 + +.L5: + ret + + +spostinc: +.L6: + <entry-point> + store.64 %arg1 -> 0[x] + load.64 %r33 <- 0[x] + load.32 %r34 <- 0[%r33] + trunc.3 %r35 <- (32) %r34 + zext.32 %r36 <- (3) %r35 + add.32 %r37 <- %r36, $1 + trunc.3 %r38 <- (32) %r37 + load.32 %r39 <- 0[%r33] + zext.32 %r40 <- (3) %r38 + and.32 %r41 <- %r39, $0xfffffff8 + or.32 %r42 <- %r41, %r40 + store.32 %r42 -> 0[%r33] + zext.32 %r43 <- (3) %r36 + phisrc.32 %phi3(return) <- %r43 + br .L7 + +.L7: + phi.32 %r44 <- %phi3(return) + ret.32 %r44 + + +spreinc: +.L8: + <entry-point> + store.64 %arg1 -> 0[x] + load.64 %r45 <- 0[x] + load.32 %r46 <- 0[%r45] + trunc.3 %r47 <- (32) %r46 + zext.32 %r48 <- (3) %r47 + add.32 %r49 <- %r48, $1 + trunc.3 %r50 <- (32) %r49 + load.32 %r51 <- 0[%r45] + zext.32 %r52 <- (3) %r50 + and.32 %r53 <- %r51, $0xfffffff8 + or.32 %r54 <- %r53, %r52 + store.32 %r54 -> 0[%r45] + zext.32 %r55 <- (3) %r50 + phisrc.32 %phi4(return) <- %r55 + br .L9 + +.L9: + phi.32 %r56 <- %phi4(return) + ret.32 %r56 + + +scpy: +.L10: + <entry-point> + store.64 %arg1 -> 0[d] + store.64 %arg2 -> 0[s] + load.64 %r57 <- 0[s] + load.32 %r58 <- 0[%r57] + trunc.3 %r59 <- (32) %r58 + load.64 %r60 <- 0[d] + load.32 %r61 <- 0[%r60] + zext.32 %r62 <- (3) %r59 + and.32 %r63 <- %r61, $0xfffffff8 + or.32 %r64 <- %r63, %r62 + store.32 %r64 -> 0[%r60] + br .L11 + +.L11: + ret + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/linear/bitfield-store.c b/usr/src/tools/smatch/src/validation/linear/bitfield-store.c new file mode 100644 index 0000000000..3d952c8dea --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/bitfield-store.c @@ -0,0 +1,22 @@ +int foo(void) +{ + struct { + int a:8; + int b:16; + int c:8; + } s = { 0xff, 0x0000, 0xff }; + + return s.b = 0x56781234; +} + +/* + * check-name: bitfield-store + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0x1234 + * + * check-error-start +linear/bitfield-store.c:9:22: warning: cast truncates bits from constant value (56781234 becomes 1234) + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/linear/bool-cast-lp32.c b/usr/src/tools/smatch/src/validation/linear/bool-cast-lp32.c new file mode 100644 index 0000000000..44a650f41e --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/bool-cast-lp32.c @@ -0,0 +1,19 @@ +extern int ffun(void); +typedef void *vdp; +typedef int *sip; + +static _Bool fvdp_i(vdp a) { return a; } +static _Bool fvdp_e(vdp a) { return (_Bool)a; } +static _Bool fsip_i(sip a) { return a; } +static _Bool fsip_e(sip a) { return (_Bool)a; } +static _Bool ffun_i(void) { return ffun; } +static _Bool ffun_e(void) { return (_Bool)ffun; } + +/* + * check-name: bool-cast-pointer + * check-command: test-linearize -m32 -fdump-ir $file + * check-known-to-fail + * + * check-output-ignore + * check-output-excludes: ptrtu\\. + */ diff --git a/usr/src/tools/smatch/src/validation/linear/bool-cast-lp64.c b/usr/src/tools/smatch/src/validation/linear/bool-cast-lp64.c new file mode 100644 index 0000000000..caf0e67879 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/bool-cast-lp64.c @@ -0,0 +1,19 @@ +extern int ffun(void); +typedef void *vdp; +typedef int *sip; + +static _Bool fvdp_i(vdp a) { return a; } +static _Bool fvdp_e(vdp a) { return (_Bool)a; } +static _Bool fsip_i(sip a) { return a; } +static _Bool fsip_e(sip a) { return (_Bool)a; } +static _Bool ffun_i(void) { return ffun; } +static _Bool ffun_e(void) { return (_Bool)ffun; } + +/* + * check-name: bool-cast-pointer + * check-command: test-linearize -m64 -fdump-ir $file + * check-assert: sizeof(void *) == 8 + * + * check-output-ignore + * check-output-excludes: ptrtu\\. + */ diff --git a/usr/src/tools/smatch/src/validation/linear/bool-cast.c b/usr/src/tools/smatch/src/validation/linear/bool-cast.c new file mode 100644 index 0000000000..1716d18b21 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/bool-cast.c @@ -0,0 +1,37 @@ +extern int fun(void); +typedef unsigned int u32; +typedef int s32; +typedef void *vdp; +typedef int *sip; +typedef double dbl; +typedef unsigned short __attribute__((bitwise)) le16; + +static _Bool fs32_i(s32 a) { return a; } +static _Bool fs32_e(s32 a) { return (_Bool)a; } +static _Bool fu32_i(u32 a) { return a; } +static _Bool fu32_e(u32 a) { return (_Bool)a; } +static _Bool fvdp_i(vdp a) { return a; } +static _Bool fvdp_e(vdp a) { return (_Bool)a; } +static _Bool fsip_i(sip a) { return a; } +static _Bool fsip_e(sip a) { return (_Bool)a; } +static _Bool ffun_i(void) { return fun; } +static _Bool ffun_e(void) { return (_Bool)fun; } +static _Bool fres_i(le16 a) { return a; } +static _Bool fres_e(le16 a) { return (_Bool)a; } +static _Bool fdbl_i(dbl a) { return a; } +static _Bool fdbl_e(dbl a) { return (_Bool)a; } + +/* + * check-name: bool-cast + * check-command: test-linearize -m64 -fdump-ir=linearize $file + * check-assert: sizeof(void*) == 8 && sizeof(long) == 8 && sizeof(double) == 8 + * + * check-output-ignore + * check-output-excludes: cast\\. + * check-output-excludes: fcvt[us]\\. + * check-output-excludes: ptrtu\\. + * check-output-excludes: [sz]ext\\. + * check-output-excludes: trunc\\. + * check-output-pattern(12): setne\\. + * check-output-pattern(2): fcmpune\\. + */ diff --git a/usr/src/tools/smatch/src/validation/linear/builtin_unreachable.c b/usr/src/tools/smatch/src/validation/linear/builtin_unreachable.c new file mode 100644 index 0000000000..4f13b892af --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/builtin_unreachable.c @@ -0,0 +1,31 @@ +void function_that_never_returns(void); + +int foo(int c) +{ + if (c) + return 1; + function_that_never_returns(); + __builtin_unreachable(); +} + +/* + * check-name: __builtin_unreachable() + * check-command: test-linearize -Wno-decl $file + * + * check-known-to-fail + * check-output-start +foo: +.L0: + <entry-point> + cbr %arg1, .L3, .L2 + +.L2: + call function_that_never_returns + unreach + +.L3: + ret.32 $1 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/linear/call-basic.c b/usr/src/tools/smatch/src/validation/linear/call-basic.c new file mode 100644 index 0000000000..3822a2674f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/call-basic.c @@ -0,0 +1,57 @@ +extern int fun(int a); + +void symbol(int a) +{ + fun(a); +} + +void pointer0(int a, int (*fun)(int)) +{ + fun(a); +} + +void pointer1(int a, int (*fun)(int)) +{ + (*fun)(a); +} + +void builtin(int a) +{ + __builtin_popcount(a); +} + +/* + * check-name: basic function calls + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +symbol: +.L0: + <entry-point> + call.32 %r2 <- fun, %arg1 + ret + + +pointer0: +.L2: + <entry-point> + call.32 %r5 <- %arg2, %arg1 + ret + + +pointer1: +.L4: + <entry-point> + call.32 %r8 <- %arg2, %arg1 + ret + + +builtin: +.L6: + <entry-point> + call.32 %r10 <- __builtin_popcount, %arg1 + ret + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/linear/call-builtin.c b/usr/src/tools/smatch/src/validation/linear/call-builtin.c new file mode 100644 index 0000000000..43e254f6d3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/call-builtin.c @@ -0,0 +1,17 @@ +typedef unsigned int u32; + +u32 ff(u32 a) { return __builtin_popcount(a); } + +u32 f0(u32 a) { return (__builtin_popcount)(a); } +u32 f1(u32 a) { return (*__builtin_popcount)(a); } // C99,C11 6.5.3.2p4 +u32 f2(u32 a) { return (**__builtin_popcount)(a); } // C99,C11 6.5.3.2p4 +u32 f3(u32 a) { return (***__builtin_popcount)(a); } // C99,C11 6.5.3.2p4 + +/* + * check-name: builtin calls + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: load + * check-output-pattern(5): call\\..*__builtin_.*, %arg1 + */ diff --git a/usr/src/tools/smatch/src/validation/linear/call-casted-pointer.c b/usr/src/tools/smatch/src/validation/linear/call-casted-pointer.c new file mode 100644 index 0000000000..60f5360527 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/call-casted-pointer.c @@ -0,0 +1,31 @@ +typedef int (*fun_t)(void*); + +int foo(void *a, void *fun) +{ + return ((fun_t)fun)(a); +} + +int bar(void *a, void *fun) +{ + return ((int (*)(void *))fun)(a); +} + +int qux(void *a, void *fun) +{ + return (*(fun_t)fun)(a); +} + +int quz(void *a, void *fun) +{ + return (*(int (*)(void *))fun)(a); +} + +/* + * check-name: call via casted function pointer + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: load + * check-output-pattern(4): ptrcast\\..* %arg2 + * check-output-pattern(4): call\\..* %arg1 + */ diff --git a/usr/src/tools/smatch/src/validation/linear/call-complex-pointer.c b/usr/src/tools/smatch/src/validation/linear/call-complex-pointer.c new file mode 100644 index 0000000000..2be35c5bef --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/call-complex-pointer.c @@ -0,0 +1,33 @@ +int foo(int p, int (*f0)(int), int (*f1)(int), int arg) +{ + return (p ? f0 : f1)(arg); +} + +/* + * check-name: call-complex-pointer + * check-command: test-linearize -m64 -Wno-decl $file + * check-assert: sizeof(void *) == 8 + * + * check-output-start +foo: +.L0: + <entry-point> + cbr %arg1, .L2, .L3 + +.L2: + phisrc.64 %phi1 <- %arg2 + br .L4 + +.L3: + ptrcast.64 %r6 <- (64) %arg3 + phisrc.64 %phi2 <- %r6 + br .L4 + +.L4: + phi.64 %r7 <- %phi1, %phi2 + call.32 %r8 <- %r7, %arg4 + ret.32 %r8 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/linear/call-direct.c b/usr/src/tools/smatch/src/validation/linear/call-direct.c new file mode 100644 index 0000000000..c3761cc5fb --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/call-direct.c @@ -0,0 +1,17 @@ +extern int fun(void); + +int ff(void) { return fun(); } + +int f0(void) { return (fun)(); } +int f1(void) { return (*fun)(); } // C99,C11 6.5.3.2p4 +int f2(void) { return (**fun)(); } // C99,C11 6.5.3.2p4 +int f3(void) { return (***fun)(); } // C99,C11 6.5.3.2p4 + +/* + * check-name: direct calls + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: load + * check-output-pattern(5): call\\..* fun + */ diff --git a/usr/src/tools/smatch/src/validation/linear/call-indirect.c b/usr/src/tools/smatch/src/validation/linear/call-indirect.c new file mode 100644 index 0000000000..4008556850 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/call-indirect.c @@ -0,0 +1,15 @@ +int gg(int (*fun)(void)) { return fun(); } + +int g0(int (*fun)(void)) { return (fun)(); } +int g1(int (*fun)(void)) { return (*fun)(); } // C99,C11 6.5.3.2p4 +int g2(int (*fun)(void)) { return (**fun)(); } // C99,C11 6.5.3.2p4 +int g3(int (*fun)(void)) { return (***fun)(); } // C99,C11 6.5.3.2p4 + +/* + * check-name: indirect calls + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: load + * check-output-pattern(5): call\\..* %arg1 + */ diff --git a/usr/src/tools/smatch/src/validation/linear/call-inline.c b/usr/src/tools/smatch/src/validation/linear/call-inline.c new file mode 100644 index 0000000000..dfd49b62c4 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/call-inline.c @@ -0,0 +1,18 @@ +static inline int fun(void) { return 42; } + +int fi(void) { return fun(); } + +int i0(void) { return (fun)(); } +int i1(void) { return (*fun)(); } // C99,C11 6.5.3.2p4 +int i2(void) { return (**fun)(); } // C99,C11 6.5.3.2p4 +int i3(void) { return (***fun)(); } // C99,C11 6.5.3.2p4 + +/* + * check-name: inline calls + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: load + * check-output-excludes: call + * check-output-pattern(5): ret\\..* \\$42 + */ diff --git a/usr/src/tools/smatch/src/validation/cast-constant-to-float.c b/usr/src/tools/smatch/src/validation/linear/cast-constant-to-float.c index 86b7ac0f7e..ac4eff0f1c 100644 --- a/usr/src/tools/smatch/src/validation/cast-constant-to-float.c +++ b/usr/src/tools/smatch/src/validation/linear/cast-constant-to-float.c @@ -13,21 +13,21 @@ double f3(void) { return -1.0; } f1: .L0: <entry-point> - set.64 %r1 <- -1.000000 + setfval.64 %r1 <- -1.000000e+00 ret.64 %r1 f2: .L2: <entry-point> - set.64 %r3 <- -1.000000 + setfval.64 %r3 <- -1.000000e+00 ret.64 %r3 f3: .L4: <entry-point> - set.64 %r5 <- -1.000000 + setfval.64 %r5 <- -1.000000e+00 ret.64 %r5 diff --git a/usr/src/tools/smatch/src/validation/cast-constants.c b/usr/src/tools/smatch/src/validation/linear/cast-constants.c index f47d6fd346..2aaee2b441 100644 --- a/usr/src/tools/smatch/src/validation/cast-constants.c +++ b/usr/src/tools/smatch/src/validation/linear/cast-constants.c @@ -53,6 +53,7 @@ static double float_2_double(void) { return (double)1.123F; } /* * check-name: cast-constants.c * check-command: test-linearize -m64 $file + * check-assert: sizeof(void *) == 8 && sizeof(long) == 8 && sizeof(double) == 8 * * check-output-start uint_2_int: @@ -286,70 +287,70 @@ vptr_2_iptr: int_2_float: .L76: <entry-point> - set.32 %r39 <- 123.000000 + setfval.32 %r39 <- 1.230000e+02 ret.32 %r39 uint_2_float: .L78: <entry-point> - set.32 %r41 <- 123.000000 + setfval.32 %r41 <- 1.230000e+02 ret.32 %r41 long_2_float: .L80: <entry-point> - set.32 %r43 <- 123.000000 + setfval.32 %r43 <- 1.230000e+02 ret.32 %r43 ulong_2_float: .L82: <entry-point> - set.32 %r45 <- 123.000000 + setfval.32 %r45 <- 1.230000e+02 ret.32 %r45 double_2_float: .L84: <entry-point> - set.32 %r47 <- 1.123000 + setfval.32 %r47 <- 1.123000e+00 ret.32 %r47 int_2_double: .L86: <entry-point> - set.64 %r49 <- 123.000000 + setfval.64 %r49 <- 1.230000e+02 ret.64 %r49 uint_2_double: .L88: <entry-point> - set.64 %r51 <- 123.000000 + setfval.64 %r51 <- 1.230000e+02 ret.64 %r51 long_2_double: .L90: <entry-point> - set.64 %r53 <- 123.000000 + setfval.64 %r53 <- 1.230000e+02 ret.64 %r53 ulong_2_double: .L92: <entry-point> - set.64 %r55 <- 123.000000 + setfval.64 %r55 <- 1.230000e+02 ret.64 %r55 float_2_double: .L94: <entry-point> - set.64 %r57 <- 1.123000 + setfval.64 %r57 <- 1.123000e+00 ret.64 %r57 diff --git a/usr/src/tools/smatch/src/validation/linear/cast-volatile.c b/usr/src/tools/smatch/src/validation/linear/cast-volatile.c new file mode 100644 index 0000000000..6d12c01507 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/cast-volatile.c @@ -0,0 +1,15 @@ +static int foo(volatile int *a, int v) +{ + *a = v; + return *a; +} + +/* + * check-name: cast-volatile + * check-command: test-linearize -fdump-ir=linearize $file + * + * check-output-ignore + * check-output-excludes: sext\\. + * check-output-excludes: zext\\. + * check-output-excludes: trunc\\. + */ diff --git a/usr/src/tools/smatch/src/validation/linear/compound-literal00.c b/usr/src/tools/smatch/src/validation/linear/compound-literal00.c new file mode 100644 index 0000000000..f3069d2ca5 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/compound-literal00.c @@ -0,0 +1,18 @@ +struct bfs { + int a: 2; + int b: 30; +}; + +int foo(void) +{ + return (struct bfs){ .a = 1, .b = 2}.b; +} + +/* + * check-name: compound-literal00.c + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$2 + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/linear/compound-literal01.c b/usr/src/tools/smatch/src/validation/linear/compound-literal01.c new file mode 100644 index 0000000000..e45fade370 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/compound-literal01.c @@ -0,0 +1,18 @@ +struct bfs { + int a: 2; + int b: 30; +}; + +int foo(void) +{ + struct bfs bf = { .a = 1, .b = 2 }; + return (struct bfs[]){bf}[0].b; +} + +/* + * check-name: compound-literal01.c + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$2 + */ diff --git a/usr/src/tools/smatch/src/validation/linear/compound-literal02.c b/usr/src/tools/smatch/src/validation/linear/compound-literal02.c new file mode 100644 index 0000000000..87b98d76b2 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/compound-literal02.c @@ -0,0 +1,19 @@ +struct bfs { + int a: 2; + int b: 30; +}; + +int bar(void) +{ + struct bfs bf = { .a = 1, .b = 4 }; + return (struct bfs[]){bf, { .a = 3, .b = 6}}[1].b; +} + +/* + * check-name: compound-literal02.c + * check-command: test-linearize -Wno-decl $file + * + * check-known-to-fail + * check-output-ignore + * check-output-contains: ret\\..*\\$6 + */ diff --git a/usr/src/tools/smatch/src/validation/linear/degen-array.c b/usr/src/tools/smatch/src/validation/linear/degen-array.c new file mode 100644 index 0000000000..32230dde3f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/degen-array.c @@ -0,0 +1,32 @@ +extern int a[3]; + +int (*fa(int i))[] { return &a; } +int *f0(int i) { return &a[0]; } +int *fd(int i) { return a; } + +/* + * check-name: degen-array + * check-command: test-linearize -m64 -Wno-decl $file + * check-assert: sizeof(void *) == 8 + * + * check-output-start +fa: +.L0: + <entry-point> + ret.64 a + + +f0: +.L2: + <entry-point> + ret.64 a + + +fd: +.L4: + <entry-point> + ret.64 a + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/linear/degen-function.c b/usr/src/tools/smatch/src/validation/linear/degen-function.c new file mode 100644 index 0000000000..e0b05e5f46 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/degen-function.c @@ -0,0 +1,52 @@ +extern int fun(int); + +typedef int (*fun_t)(int); + +fun_t fa(void) { return &fun; } +fun_t f0(void) { return fun; } +fun_t f1(void) { return *fun; } + +/* + * check-name: degen-function + * check-command: test-linearize -m64 -Wno-decl -fdump-ir=linearize $file + * check-assert: sizeof(void *) == 8 + * + * check-output-start +fa: +.L0: + <entry-point> + symaddr.64 %r1 <- fun + phisrc.64 %phi1(return) <- %r1 + br .L1 + +.L1: + phi.64 %r2 <- %phi1(return) + ret.64 %r2 + + +f0: +.L2: + <entry-point> + symaddr.64 %r3 <- fun + phisrc.64 %phi2(return) <- %r3 + br .L3 + +.L3: + phi.64 %r4 <- %phi2(return) + ret.64 %r4 + + +f1: +.L4: + <entry-point> + symaddr.64 %r5 <- fun + phisrc.64 %phi3(return) <- %r5 + br .L5 + +.L5: + phi.64 %r6 <- %phi3(return) + ret.64 %r6 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/linear/degen-log-not.c b/usr/src/tools/smatch/src/validation/linear/degen-log-not.c new file mode 100644 index 0000000000..a982e34b20 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/degen-log-not.c @@ -0,0 +1,40 @@ +extern int arr[]; +int test_arr_addr(int i) +{ + if (!&arr) return 1; + return 0; +} + +int test_arr_addr0(int i) +{ + if (!&arr[0]) return 1; + return 0; +} + +int test_arr_degen(int i) +{ + if (!arr) return 1; + return 0; +} + +extern int fun(void); +int test_fun_addr(int i) +{ + if (!&fun) return 1; + return 0; +} + +int test_fun_degen(int i) +{ + if (!fun) return 1; + return 0; +} + +/* + * check-name: degenerate logical-not + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: load + * check-output-excludes: VOID + */ diff --git a/usr/src/tools/smatch/src/validation/linear/deref-ptr-ptr.c b/usr/src/tools/smatch/src/validation/linear/deref-ptr-ptr.c new file mode 100644 index 0000000000..1342990c4a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/deref-ptr-ptr.c @@ -0,0 +1,27 @@ +char *foo(char **pfmt) +{ + return ++*pfmt; +} + +/* + * check-name: deref-ptr-ptr + * check-command: test-linearize -m64 -Wno-decl $file + * check-assert: sizeof(void *) == 8 + * + * check-output-excludes: load[^.] + * check-output-contains: load\\. + * check-output-excludes: store[^.] + * check-output-contains: store\\. + * + * check-output-start +foo: +.L0: + <entry-point> + load.64 %r2 <- 0[%arg1] + add.64 %r3 <- %r2, $1 + store.64 %r3 -> 0[%arg1] + ret.64 %r3 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/fp-vs-ptrcast.c b/usr/src/tools/smatch/src/validation/linear/fp-vs-ptrcast.c index 817aee5c90..817aee5c90 100644 --- a/usr/src/tools/smatch/src/validation/fp-vs-ptrcast.c +++ b/usr/src/tools/smatch/src/validation/linear/fp-vs-ptrcast.c diff --git a/usr/src/tools/smatch/src/validation/linear/fp2i-cast.c b/usr/src/tools/smatch/src/validation/linear/fp2i-cast.c new file mode 100644 index 0000000000..c85c4ccd7e --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/fp2i-cast.c @@ -0,0 +1,31 @@ +#if __SIZEOF_INT__ == __SIZEOF_FLOAT__ +typedef signed int si; +typedef unsigned int ui; +#else +#error "no float-sized integer type" +#endif + +#if __SIZEOF_LONG_LONG__ == __SIZEOF_DOUBLE__ +typedef signed long long sl; +typedef unsigned long long ul; +#else +#error "no double-sized integer type" +#endif + +si f2si(float a) { return a; } +ui f2ui(float a) { return a; } +sl f2sl(float a) { return a; } +ul f2ul(float a) { return a; } +si d2si(double a) { return a; } +ui d2ui(double a) { return a; } +sl d2sl(double a) { return a; } +ul d2ul(double a) { return a; } + +/* + * check-name: fp2i cast + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(4): fcvts\\. + * check-output-pattern(4): fcvtu\\. + */ diff --git a/usr/src/tools/smatch/src/validation/linear/logical-phi0.c b/usr/src/tools/smatch/src/validation/linear/logical-phi0.c new file mode 100644 index 0000000000..96a47dba41 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/logical-phi0.c @@ -0,0 +1,48 @@ +int a(void); +int b(void); +int c(void); + +static int laa(void) +{ + return (a() && b()) && c(); +} + +static int lao(void) +{ + return (a() && b()) || c(); +} + +static int loa(void) +{ + return (a() || b()) && c(); +} + +static int loo(void) +{ + return (a() || b()) || c(); +} + +static int raa(void) +{ + return a() && (b() && c()); +} + +static int rao(void) +{ + return a() && (b() || c()); +} + +static int roa(void) +{ + return a() || (b() && c()); +} + +static int roo(void) +{ + return a() || (b() || c()); +} + +/* + * check-name: bad-logical-phi0 + * check-command: sparse -vir -flinearize=last $file + */ diff --git a/usr/src/tools/smatch/src/validation/linear/logical.c b/usr/src/tools/smatch/src/validation/linear/logical.c new file mode 100644 index 0000000000..81a9ff42e6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/logical.c @@ -0,0 +1,260 @@ +struct S { + int :1; + signed int s:2; + unsigned int u:3; + long l; + double d; +}; + +int os(int i, struct S *b) { return i || b->s; } +int ou(int i, struct S *b) { return i || b->u; } +int ol(int i, struct S *b) { return i || b->l; } +int od(int i, struct S *b) { return i || b->d; } + +int as(int i, struct S *b) { return i && b->s; } +int au(int i, struct S *b) { return i && b->u; } +int al(int i, struct S *b) { return i && b->l; } +int ad(int i, struct S *b) { return i && b->d; } + +/* + * check-name: logical + * check-command: test-linearize -m64 -fdump-ir -Wno-decl $file + * check-assert: sizeof(void *) == 8 && sizeof(long) == 8 && sizeof(double) == 8 + * + * check-output-start +os: +.L0: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + load.32 %r2 <- 0[i] + setne.1 %r3 <- %r2, $0 + phisrc.32 %phi1 <- $1 + cbr %r3, .L3, .L2 + +.L2: + load.64 %r4 <- 0[b] + load.32 %r5 <- 0[%r4] + lsr.32 %r6 <- %r5, $1 + trunc.2 %r7 <- (32) %r6 + setne.1 %r8 <- %r7, $0 + zext.32 %r9 <- (1) %r8 + phisrc.32 %phi2 <- %r9 + br .L3 + +.L3: + phi.32 %r1 <- %phi1, %phi2 + phisrc.32 %phi3(return) <- %r1 + br .L1 + +.L1: + phi.32 %r10 <- %phi3(return) + ret.32 %r10 + + +ou: +.L4: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + load.32 %r12 <- 0[i] + setne.1 %r13 <- %r12, $0 + phisrc.32 %phi4 <- $1 + cbr %r13, .L7, .L6 + +.L6: + load.64 %r14 <- 0[b] + load.32 %r15 <- 0[%r14] + lsr.32 %r16 <- %r15, $3 + trunc.3 %r17 <- (32) %r16 + setne.1 %r18 <- %r17, $0 + zext.32 %r19 <- (1) %r18 + phisrc.32 %phi5 <- %r19 + br .L7 + +.L7: + phi.32 %r11 <- %phi4, %phi5 + phisrc.32 %phi6(return) <- %r11 + br .L5 + +.L5: + phi.32 %r20 <- %phi6(return) + ret.32 %r20 + + +ol: +.L8: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + load.32 %r22 <- 0[i] + setne.1 %r23 <- %r22, $0 + phisrc.32 %phi7 <- $1 + cbr %r23, .L11, .L10 + +.L10: + load.64 %r24 <- 0[b] + load.64 %r25 <- 8[%r24] + setne.1 %r26 <- %r25, $0 + zext.32 %r27 <- (1) %r26 + phisrc.32 %phi8 <- %r27 + br .L11 + +.L11: + phi.32 %r21 <- %phi7, %phi8 + phisrc.32 %phi9(return) <- %r21 + br .L9 + +.L9: + phi.32 %r28 <- %phi9(return) + ret.32 %r28 + + +od: +.L12: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + load.32 %r30 <- 0[i] + setne.1 %r31 <- %r30, $0 + phisrc.32 %phi10 <- $1 + cbr %r31, .L15, .L14 + +.L14: + load.64 %r32 <- 0[b] + load.64 %r33 <- 16[%r32] + setfval.64 %r34 <- 0.000000e+00 + fcmpune.1 %r35 <- %r33, %r34 + zext.32 %r36 <- (1) %r35 + phisrc.32 %phi11 <- %r36 + br .L15 + +.L15: + phi.32 %r29 <- %phi10, %phi11 + phisrc.32 %phi12(return) <- %r29 + br .L13 + +.L13: + phi.32 %r37 <- %phi12(return) + ret.32 %r37 + + +as: +.L16: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + load.32 %r39 <- 0[i] + setne.1 %r40 <- %r39, $0 + phisrc.32 %phi13 <- $0 + cbr %r40, .L18, .L19 + +.L18: + load.64 %r41 <- 0[b] + load.32 %r42 <- 0[%r41] + lsr.32 %r43 <- %r42, $1 + trunc.2 %r44 <- (32) %r43 + setne.1 %r45 <- %r44, $0 + zext.32 %r46 <- (1) %r45 + phisrc.32 %phi14 <- %r46 + br .L19 + +.L19: + phi.32 %r38 <- %phi13, %phi14 + phisrc.32 %phi15(return) <- %r38 + br .L17 + +.L17: + phi.32 %r47 <- %phi15(return) + ret.32 %r47 + + +au: +.L20: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + load.32 %r49 <- 0[i] + setne.1 %r50 <- %r49, $0 + phisrc.32 %phi16 <- $0 + cbr %r50, .L22, .L23 + +.L22: + load.64 %r51 <- 0[b] + load.32 %r52 <- 0[%r51] + lsr.32 %r53 <- %r52, $3 + trunc.3 %r54 <- (32) %r53 + setne.1 %r55 <- %r54, $0 + zext.32 %r56 <- (1) %r55 + phisrc.32 %phi17 <- %r56 + br .L23 + +.L23: + phi.32 %r48 <- %phi16, %phi17 + phisrc.32 %phi18(return) <- %r48 + br .L21 + +.L21: + phi.32 %r57 <- %phi18(return) + ret.32 %r57 + + +al: +.L24: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + load.32 %r59 <- 0[i] + setne.1 %r60 <- %r59, $0 + phisrc.32 %phi19 <- $0 + cbr %r60, .L26, .L27 + +.L26: + load.64 %r61 <- 0[b] + load.64 %r62 <- 8[%r61] + setne.1 %r63 <- %r62, $0 + zext.32 %r64 <- (1) %r63 + phisrc.32 %phi20 <- %r64 + br .L27 + +.L27: + phi.32 %r58 <- %phi19, %phi20 + phisrc.32 %phi21(return) <- %r58 + br .L25 + +.L25: + phi.32 %r65 <- %phi21(return) + ret.32 %r65 + + +ad: +.L28: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + load.32 %r67 <- 0[i] + setne.1 %r68 <- %r67, $0 + phisrc.32 %phi22 <- $0 + cbr %r68, .L30, .L31 + +.L30: + load.64 %r69 <- 0[b] + load.64 %r70 <- 16[%r69] + setfval.64 %r71 <- 0.000000e+00 + fcmpune.1 %r72 <- %r70, %r71 + zext.32 %r73 <- (1) %r72 + phisrc.32 %phi23 <- %r73 + br .L31 + +.L31: + phi.32 %r66 <- %phi22, %phi23 + phisrc.32 %phi24(return) <- %r66 + br .L29 + +.L29: + phi.32 %r74 <- %phi24(return) + ret.32 %r74 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/linear/missing-return0.c b/usr/src/tools/smatch/src/validation/linear/missing-return0.c new file mode 100644 index 0000000000..77ab5abd67 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/missing-return0.c @@ -0,0 +1,10 @@ +static int foo(int a) +{ + if (a) + return 1; +} + +/* + * check-name: missing-return0 + * check-command: sparse -vir -flinearize=last $file + */ diff --git a/usr/src/tools/smatch/src/validation/linear/missing-return1.c b/usr/src/tools/smatch/src/validation/linear/missing-return1.c new file mode 100644 index 0000000000..4a8a9517da --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/missing-return1.c @@ -0,0 +1,15 @@ +static inline int fun(int a) +{ + if (a) + return 1; +} + +static int foo(int a) +{ + return fun(a); +} + +/* + * check-name: missing-return1 + * check-command: sparse -vir -flinearize=last $file + */ diff --git a/usr/src/tools/smatch/src/validation/linear/missing-return2.c b/usr/src/tools/smatch/src/validation/linear/missing-return2.c new file mode 100644 index 0000000000..395dcc143e --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/missing-return2.c @@ -0,0 +1,11 @@ +static int foo(int a) +{ + switch (a) + case 3: + return 4; +} + +/* + * check-name: missing-return2 + * check-command: sparse -vir -flinearize=last $file + */ diff --git a/usr/src/tools/smatch/src/validation/linear/missing-return3.c b/usr/src/tools/smatch/src/validation/linear/missing-return3.c new file mode 100644 index 0000000000..b32e5eea88 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/missing-return3.c @@ -0,0 +1,18 @@ +static int foo(int a) +{ + if (a) + return; +} + +static void ref(void) +{ +} + +/* + * check-name: missing-return3 + * check-command: sparse -vir -flinearize=last $file + * + * check-error-start +linear/missing-return3.c:4:17: error: return with no return value + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/linear/missing-return4.c b/usr/src/tools/smatch/src/validation/linear/missing-return4.c new file mode 100644 index 0000000000..779893a0e7 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/missing-return4.c @@ -0,0 +1,14 @@ +static int foo(int a) +{ + int r = a; + r; +} + +/* + * check-name: missing-return4 + * check-command: test-linearize -Wno-decl $file + * + * check-error-ignore + * check-output-ignore + * check-output-contains: ret\\..*UNDEF + */ diff --git a/usr/src/tools/smatch/src/validation/linear/missing-return5.c b/usr/src/tools/smatch/src/validation/linear/missing-return5.c new file mode 100644 index 0000000000..e5504a194c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/missing-return5.c @@ -0,0 +1,23 @@ +int foo(int p) +{ + if (p) + return 0; +} + +int bar(int p) +{ + if (p) + return 0; + p++; +} + +/* + * check-name: missing/undef return + * check-command: test-linearize -Wno-decl -fdump-ir=linearize $file + * + * check-output-ignore + * check-output-pattern(2): phi\\..*,.* + * check-output-pattern(2): phisrc\\..*\\$0 + * check-output-pattern(2): phisrc\\..*UNDEF + * check-output-excludes: ret\\..*\\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/linear/non-const-case.c b/usr/src/tools/smatch/src/validation/linear/non-const-case.c new file mode 100644 index 0000000000..7291589cb7 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/non-const-case.c @@ -0,0 +1,37 @@ +static int foo(int a) +{ + switch (a) { + case 0: + return a; + case a: + return 0; + case (a - a): + return 1; + default: + return a; + } +} + +static int bar(int a) +{ + switch (a) { + case 0: + break; + case a: + a++; +label: + return a; + } + + goto label; +} + + +/* + * check-name: non-const-case + * check-command: test-linearize -Wno-decl $file + * + * check-error-ignore + * check-output-ignore + * check-output-excludes:switch \\. + */ diff --git a/usr/src/tools/smatch/src/validation/linear/phi-order01.c b/usr/src/tools/smatch/src/validation/linear/phi-order01.c new file mode 100644 index 0000000000..0c4004fea0 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/phi-order01.c @@ -0,0 +1,16 @@ +int fun(void); + +static int foo(int a) +{ + return a && fun(); +} + +static int bar(int a) +{ + return a || fun(); +} + +/* + * check-name: phi-order01 + * check-command: sparse -vir -flinearize=last $file + */ diff --git a/usr/src/tools/smatch/src/validation/linear/phi-order02.c b/usr/src/tools/smatch/src/validation/linear/phi-order02.c new file mode 100644 index 0000000000..d217ae452c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/phi-order02.c @@ -0,0 +1,16 @@ +int fun(void); + +static int foo(int a) { return 0 || fun(); } +static int bar(int a) { return 1 || fun(); } +static int baz(int a) { return 0 && fun(); } +static int qux(int a) { return 1 && fun(); } + +static int oof(int a) { return fun() || 1; } +static int rab(int a) { return fun() || 0; } +static int zab(int a) { return fun() && 1; } +static int xuq(int a) { return fun() && 0; } + +/* + * check-name: phi-order02 + * check-command: sparse -vir -flinearize=last $file + */ diff --git a/usr/src/tools/smatch/src/validation/linear/phi-order03.c b/usr/src/tools/smatch/src/validation/linear/phi-order03.c new file mode 100644 index 0000000000..24ae10e7b7 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/phi-order03.c @@ -0,0 +1,8 @@ +int fun(void); + +static int foo(void) { return ((0 || fun()) && fun()); } + +/* + * check-name: phi-order03 + * check-command: sparse -vir -flinearize=last $file + */ diff --git a/usr/src/tools/smatch/src/validation/linear/phi-order04.c b/usr/src/tools/smatch/src/validation/linear/phi-order04.c new file mode 100644 index 0000000000..7548537a92 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/phi-order04.c @@ -0,0 +1,12 @@ +static void foo(int *b) +{ + if (1) { + int c; + b = &c; + } +} + +/* + * check-name: phi-order04 + * check-command: sparse -vir -flinearize=last $file + */ diff --git a/usr/src/tools/smatch/src/validation/linear/range-op.c b/usr/src/tools/smatch/src/validation/linear/range-op.c new file mode 100644 index 0000000000..4472bb3304 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/range-op.c @@ -0,0 +1,31 @@ +static void foo(int a) +{ + __range__(a, 0, 8); +} + +static void bar(int a, int b, int c) +{ + __range__(a, b, c); +} + +/* + * check-name: range-op + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + range-check %arg1 between $0..$8 + ret + + +bar: +.L2: + <entry-point> + range-check %arg1 between %arg2..%arg3 + ret + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/linear/unexamined-base-type.c b/usr/src/tools/smatch/src/validation/linear/unexamined-base-type.c new file mode 100644 index 0000000000..142139a2e4 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/unexamined-base-type.c @@ -0,0 +1,36 @@ +# define __force __attribute__((force)) + +struct s { + int a; +}; + +static int foo(struct s *s) +{ + return (*((typeof(s->a) __force *) &s->a)) & 1; +} + +static void bar(struct s *d, struct s *s1, struct s *s2) +{ + *d = *s1, *d = *s2; +} + +/* + * check-name: unexamined base type + * check-command: test-linearize -Wno-decl $file + * check-description: + * Test case for missing examine in evaluate_dereference()'s + * target base type. In this case, the loaded value has a + * a null size, giving the wrongly generated code for foo(): + * ptrcast.64 %r3 <- (64) %arg1 + * load %r4 <- 0[%r3] + * ^^^ !! WRONG !! + * cast.32 %r5 <- (0) %r4 + * ^^^ !! WRONG !! + * and.32 %r6 <- %r5, $1 + * ret.32 %r6 + * + * check-output-ignore + * check-output-excludes: load[^.] + * check-output-excludes: cast\\..*(0) + * check-output-excludes: store[^.] + */ diff --git a/usr/src/tools/smatch/src/validation/linear/unreachable-label0.c b/usr/src/tools/smatch/src/validation/linear/unreachable-label0.c new file mode 100644 index 0000000000..695e5cb072 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/linear/unreachable-label0.c @@ -0,0 +1,19 @@ +static int foo(int a) +{ + goto label; + switch(a) { + default: +label: + break; + } + return 0; +} + +/* + * check-name: unreachable-label0 + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-contains: ret\\. + * check-output-excludes: END + */ diff --git a/usr/src/tools/smatch/src/validation/loop-linearization.c b/usr/src/tools/smatch/src/validation/loop-linearization.c deleted file mode 100644 index 25c6dfb870..0000000000 --- a/usr/src/tools/smatch/src/validation/loop-linearization.c +++ /dev/null @@ -1,136 +0,0 @@ -extern int p(int); - -static int ffor(void) -{ - int i; - for (int i = 0; i < 10; i++) { - if (!p(i)) - return 0; - } - return 1; -} - -static int fwhile(void) -{ - int i = 0; - while (i < 10) { - if (!p(i)) - return 0; - i++; - } - return 1; -} - -static int fdo(void) -{ - int i = 0; - do { - if (!p(i)) - return 0; - } while (i++ < 10); - return 1; -} - -/* - * check-name: loop-linearization - * check-command: test-linearize $file - * - * check-output-start -ffor: -.L0: - <entry-point> - phisrc.32 %phi5(i) <- $0 - br .L4 - -.L4: - phi.32 %r1(i) <- %phi5(i), %phi6(i) - setlt.32 %r2 <- %r1(i), $10 - cbr %r2, .L1, .L3 - -.L1: - call.32 %r4 <- p, %r1(i) - cbr %r4, .L2, .L5 - -.L5: - phisrc.32 %phi1(return) <- $0 - br .L7 - -.L2: - add.32 %r7 <- %r1(i), $1 - phisrc.32 %phi6(i) <- %r7 - br .L4 - -.L3: - phisrc.32 %phi2(return) <- $1 - br .L7 - -.L7: - phi.32 %r5 <- %phi1(return), %phi2(return) - ret.32 %r5 - - -fwhile: -.L8: - <entry-point> - phisrc.32 %phi11(i) <- $0 - br .L12 - -.L12: - phi.32 %r8(i) <- %phi11(i), %phi12(i) - setlt.32 %r9 <- %r8(i), $10 - cbr %r9, .L9, .L11 - -.L9: - call.32 %r11 <- p, %r8(i) - cbr %r11, .L14, .L13 - -.L13: - phisrc.32 %phi7(return) <- $0 - br .L15 - -.L14: - add.32 %r14 <- %r8(i), $1 - phisrc.32 %phi12(i) <- %r14 - br .L12 - -.L11: - phisrc.32 %phi8(return) <- $1 - br .L15 - -.L15: - phi.32 %r12 <- %phi7(return), %phi8(return) - ret.32 %r12 - - -fdo: -.L16: - <entry-point> - phisrc.32 %phi16(i) <- $0 - br .L17 - -.L17: - phi.32 %r15(i) <- %phi16(i), %phi17(i) - call.32 %r16 <- p, %r15(i) - cbr %r16, .L18, .L20 - -.L20: - phisrc.32 %phi13(return) <- $0 - br .L22 - -.L18: - add.32 %r19 <- %r15(i), $1 - setlt.32 %r20 <- %r15(i), $10 - phisrc.32 %phi17(i) <- %r19 - cbr %r20, .L17, .L19 - -.L19: - phisrc.32 %phi14(return) <- $1 - br .L22 - -.L22: - phi.32 %r17 <- %phi13(return), %phi14(return) - ret.32 %r17 - - - * check-output-end - */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/address-used00.c b/usr/src/tools/smatch/src/validation/mem2reg/address-used00.c new file mode 100644 index 0000000000..1501bc1ca2 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/address-used00.c @@ -0,0 +1,18 @@ +int foo(int **g, int j) +{ + int i = 1; + int *a; + int **p; + + a = &i; + p = &a; + *p[0] = 0; + return i; +} + +/* + * check-name: address-used00 + * check-command: test-linearize -Wno-decl -fdump-ir=final $file + * check-output-ignore + * check-output-excludes: ret\\..* \\$1 + */ diff --git a/usr/src/tools/smatch/src/validation/alias-distinct.c b/usr/src/tools/smatch/src/validation/mem2reg/alias-distinct.c index 42937b24b5..42937b24b5 100644 --- a/usr/src/tools/smatch/src/validation/alias-distinct.c +++ b/usr/src/tools/smatch/src/validation/mem2reg/alias-distinct.c diff --git a/usr/src/tools/smatch/src/validation/alias-mixed.c b/usr/src/tools/smatch/src/validation/mem2reg/alias-mixed.c index 0cfbe36b8a..0cfbe36b8a 100644 --- a/usr/src/tools/smatch/src/validation/alias-mixed.c +++ b/usr/src/tools/smatch/src/validation/mem2reg/alias-mixed.c diff --git a/usr/src/tools/smatch/src/validation/alias-same.c b/usr/src/tools/smatch/src/validation/mem2reg/alias-same.c index 55cf42445f..55cf42445f 100644 --- a/usr/src/tools/smatch/src/validation/alias-same.c +++ b/usr/src/tools/smatch/src/validation/mem2reg/alias-same.c diff --git a/usr/src/tools/smatch/src/validation/mem2reg/broken-phi02.c b/usr/src/tools/smatch/src/validation/mem2reg/broken-phi02.c new file mode 100644 index 0000000000..5aa650716d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/broken-phi02.c @@ -0,0 +1,27 @@ +int foo(int a, int b) +{ + int x; + int i; + + if (a) + i = 0; + else + i = 1; + + x = 0; + if (b) + x = i; + return x; +} + +/* + * check-name: broken-phi02 + * check-description: + * This is an indirect test to check correctness of phi-node placement. + * The misplaced phi-node for 'i' (not at the meet point but where 'i' + * is used) causes a missed select-conversion at later stage. + * + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * check-output-contains: select\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/broken-phi03.c b/usr/src/tools/smatch/src/validation/mem2reg/broken-phi03.c new file mode 100644 index 0000000000..eff1ff8a88 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/broken-phi03.c @@ -0,0 +1,28 @@ +int foo(int a, int b) +{ + int x; + int i; + + switch (a) { + case 0: i = 0; break; + case 1: i = 1; break; + default: i = -1; break; + } + + x = 0; + if (b) + x = i; + return x; +} + +/* + * check-name: broken-phi03 + * check-description: + * This is an indirect test to check correctness of phi-node placement. + * The misplaced phi-node for 'i' (not at the meet point but where 'i' + * is used) causes a missed select-conversion at later stage. + * + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * check-output-contains: select\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/cond-expr.c b/usr/src/tools/smatch/src/validation/mem2reg/cond-expr.c new file mode 100644 index 0000000000..8acb00ac95 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/cond-expr.c @@ -0,0 +1,14 @@ +int fun(int); + +int foo(int a, int b, int c) +{ + return a ? fun(b) : fun(c); +} + +/* + * check-name: cond-expr + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-pattern(2): phi\\. + * check-output-pattern(3): phisrc\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/cond-expr5.c b/usr/src/tools/smatch/src/validation/mem2reg/cond-expr5.c new file mode 100644 index 0000000000..a3ce5e3a95 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/cond-expr5.c @@ -0,0 +1,21 @@ +int foo(int p, int q, int a) +{ + if (p) + a = 0; + if (q) + a = 1; + + return a; +} + +/* + * check-name: cond-expr5 + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * + * check-output-ignore + * check-output-excludes: load\\. + * check-output-excludes: store\\. + * check-output-excludes: phi\\..*, .*, .* + * check-output-pattern(3): phi\\. + * check-output-pattern(5): phisrc\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/dead-phisrc.c b/usr/src/tools/smatch/src/validation/mem2reg/dead-phisrc.c new file mode 100644 index 0000000000..4e4f6a8b4b --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/dead-phisrc.c @@ -0,0 +1,17 @@ +static void foo(void) +{ + extern int *a; + + if (a || *a) + ; + if (a[0] || a[1]) + ; +} + +/* + * check-name: dead-phisrc + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-excludes: phisrc + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/global-direct-undef.c b/usr/src/tools/smatch/src/validation/mem2reg/global-direct-undef.c new file mode 100644 index 0000000000..34960e74dc --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/global-direct-undef.c @@ -0,0 +1,23 @@ +int a, c, d; + +int foo(void) +{ + int b, e; + if (a) + b = c; + else + b = d; + if (c) + a = b; + if (b) + e = a; + return e; +} + +/* + * check-name: global direct undef + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-pattern(4,5): load\\. + * check-output-pattern(1): store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/global-direct.c b/usr/src/tools/smatch/src/validation/mem2reg/global-direct.c new file mode 100644 index 0000000000..ea5d42dcf8 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/global-direct.c @@ -0,0 +1,23 @@ +int a, c, d; + +int foo(void) +{ + int b, e = 0; + if (a) + b = c; + else + b = d; + if (c) + a = b; + if (b) + e = a; + return e; +} + +/* + * check-name: global direct + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-pattern(4,5): load\\. + * check-output-pattern(1): store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/global-loop.c b/usr/src/tools/smatch/src/validation/mem2reg/global-loop.c new file mode 100644 index 0000000000..a232f7edf8 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/global-loop.c @@ -0,0 +1,20 @@ +struct s { + int c; + int a[]; +} s; +int f; + +void fun(void); +void foo(void) +{ + for (f = 1;;) + if (s.a[f]) + fun(); +} + +/* + * check-name: global var as loop index + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-contains: load\\..*\\[f\\] + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/global-noalias.c b/usr/src/tools/smatch/src/validation/mem2reg/global-noalias.c new file mode 100644 index 0000000000..b78b51174d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/global-noalias.c @@ -0,0 +1,21 @@ +int a, b, c, d, e; + +void foo(void) +{ + if (a) + b = c; + else + b = d; + if (c) + a = b; + if (b) + e = a; +} + +/* + * check-name: global no-alias + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-pattern(4,7): load\\. + * check-output-pattern(4): store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/global-pointer.c b/usr/src/tools/smatch/src/validation/mem2reg/global-pointer.c new file mode 100644 index 0000000000..d312577a32 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/global-pointer.c @@ -0,0 +1,26 @@ +int a, c, d; + +int foo_ptr(void) +{ + int b, *bp = &b; + int e, *ep = &e; + + if (a) + *bp = c; + else + *bp = d; + if (c) + a = *bp; + if (b) + e = a; + return e; +} + +/* + * check-name: global pointer + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-known-to-fail + * check-output-ignore + * check-output-pattern(4,5): load\\. + * check-output-pattern(3): store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/if-direct.c b/usr/src/tools/smatch/src/validation/mem2reg/if-direct.c new file mode 100644 index 0000000000..1b5a07ccd8 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/if-direct.c @@ -0,0 +1,19 @@ +int foo(int c, int a, int b) +{ + int l; + + if (c) + l = a; + else + l = b; + + return l; +} + +/* + * check-name: if-then-else direct + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-excludes: load\\. + * check-output-contains: phi\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/if-pointer.c b/usr/src/tools/smatch/src/validation/mem2reg/if-pointer.c new file mode 100644 index 0000000000..acfceb718c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/if-pointer.c @@ -0,0 +1,21 @@ +int foo(int c, int a, int b) +{ + int l, *p = &l; + + if (c) + *p = a; + else + *p = b; + + return l + *p; +} + +/* + * check-name: if-then-else pointer + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-known-to-fail + * check-output-ignore + * check-output-excludes: load\\. + * check-output-excludes: store\\. + * check-output-contains: phi\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/init-global-array.c b/usr/src/tools/smatch/src/validation/mem2reg/init-global-array.c new file mode 100644 index 0000000000..51ca50e353 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/init-global-array.c @@ -0,0 +1,21 @@ +struct s { + int a[2]; +}; + + +static struct s s; + +static int sarray(void) +{ + s.a[1] = 1; + return s.a[1]; +} + +/* + * check-name: init global array + * check-command: test-linearize $file + * check-output-ignore + * check-output-excludes: load\\. + * check-output-pattern(1): store\\. + * check-output-pattern(1): ret.32 *\\$1 + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/init-local-array.c b/usr/src/tools/smatch/src/validation/mem2reg/init-local-array.c new file mode 100644 index 0000000000..639a74f100 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/init-local-array.c @@ -0,0 +1,28 @@ +static int array(void) +{ + int a[2]; + + a[1] = 1; + a[0] = 0; + return a[1]; +} + +static int sarray(void) +{ + struct { + int a[2]; + } s; + + s.a[1] = 1; + s.a[0] = 0; + return s.a[1]; +} + +/* + * check-name: init local array + * check-command: test-linearize $file + * check-output-ignore + * check-output-excludes: load + * check-output-excludes: store + * check-output-pattern(2): ret.32 *\\$1 + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/init-local-union0.c b/usr/src/tools/smatch/src/validation/mem2reg/init-local-union0.c new file mode 100644 index 0000000000..3a57e781f8 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/init-local-union0.c @@ -0,0 +1,18 @@ +double uintfloat(void) +{ + union { + int a; + double f; + } s; + + s.a = 1; + return s.f; +} + +/* + * check-name: init-local union 0 + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-pattern(1): store\\.32 + * check-output-pattern(1): load\\.64 + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/init-local-union1.c b/usr/src/tools/smatch/src/validation/mem2reg/init-local-union1.c new file mode 100644 index 0000000000..925b0a7376 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/init-local-union1.c @@ -0,0 +1,32 @@ +double uintfloat(void) +{ + union { + int a; + double f; + } s; + + s.a = 1; + return s.f; +} + + +int uarray(void) +{ + union { + double d; + int a[2]; + } s; + + s.d = 1; + return s.a[0]; +} + +/* + * check-name: init-local union 1 + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-pattern(1): store\\.32 + * check-output-pattern(1): load\\.64 + * check-output-pattern(1): store\\.64 + * check-output-pattern(1): load\\.32 + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/init-local32.c b/usr/src/tools/smatch/src/validation/mem2reg/init-local32.c new file mode 100644 index 0000000000..9a65c20583 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/init-local32.c @@ -0,0 +1,27 @@ +int ssimple(void) +{ + struct { + int a; + } s; + + s.a = 1; + return s.a; +} + +double sdouble(void) +{ + struct { + double a; + } s; + + s.a = 1.23; + return s.a; +} + +/* + * check-name: init-local32 + * check-command: test-linearize -Wno-decl -m32 -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-excludes: load\\. + * check-output-excludes: store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/init-local64.c b/usr/src/tools/smatch/src/validation/mem2reg/init-local64.c new file mode 100644 index 0000000000..72f89742bd --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/init-local64.c @@ -0,0 +1,27 @@ +int ssimple(void) +{ + struct { + int a; + } s; + + s.a = 1; + return s.a; +} + +double sdouble(void) +{ + struct { + double a; + } s; + + s.a = 1.23; + return s.a; +} + +/* + * check-name: init-local64 + * check-command: test-linearize -Wno-decl -m64 -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-excludes: load\\. + * check-output-excludes: store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/load-dead.c b/usr/src/tools/smatch/src/validation/mem2reg/load-dead.c new file mode 100644 index 0000000000..1165fa1eab --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/load-dead.c @@ -0,0 +1,18 @@ +int fun(int); + +static inline int fake(void) +{ +} + +static void foo(int a) +{ + 0 || fun((a, fake(), a)); +} + +/* + * check-name: load-dead + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: VOID + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/load-deadborn.c b/usr/src/tools/smatch/src/validation/mem2reg/load-deadborn.c new file mode 100644 index 0000000000..fa0baeae84 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/load-deadborn.c @@ -0,0 +1,9 @@ +static void foo(int a) +{ + return; + a; +} + +/* + * check-name: load-deadborn + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/loop00.c b/usr/src/tools/smatch/src/validation/mem2reg/loop00.c new file mode 100644 index 0000000000..de33d9f643 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/loop00.c @@ -0,0 +1,16 @@ +int loop00(int n) +{ + int i, r = 0; + + for (i = 1; i <= n; ++i) + r += i; + return r; +} + +/* + * check-name: loop00 + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-excludes: store\\. + * check-output-excludes: load\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/loop01-global.c b/usr/src/tools/smatch/src/validation/mem2reg/loop01-global.c new file mode 100644 index 0000000000..b67981378d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/loop01-global.c @@ -0,0 +1,18 @@ +extern int g; + +void fun(void); +void loop01(void) +{ + int i; + for (i = 0; i <= 2;) + if (g) + fun(); +} + +/* + * check-name: loop01 global + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-excludes: load\\..*\\[i\\] + * check-output-contains: load\\..*\\[g\\] + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/loop02-array.c b/usr/src/tools/smatch/src/validation/mem2reg/loop02-array.c new file mode 100644 index 0000000000..13b0aeaf9f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/loop02-array.c @@ -0,0 +1,23 @@ + + +int foo(int i[]) +{ + int j = 1; + i[0] = 6; + + do { + if (i[0] != 6) + i[0]++; + i[0]++; + } while (i[0] != j); + + return j; +} + +/* + * check-name: loop02 array + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-pattern(0,4): load\\. + * check-output-pattern(1,3): store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/loop02-global.c b/usr/src/tools/smatch/src/validation/mem2reg/loop02-global.c new file mode 100644 index 0000000000..b627b33d18 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/loop02-global.c @@ -0,0 +1,22 @@ +int i; + +int foo(void) +{ + int j = 1; + i = 6; + + do { + if (i != 6) + i++; + i++; + } while (i != j); + + return j; +} + +/* + * check-name: loop02 global + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * check-output-excludes: load\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/loop02-local.c b/usr/src/tools/smatch/src/validation/mem2reg/loop02-local.c new file mode 100644 index 0000000000..a1bd602b74 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/loop02-local.c @@ -0,0 +1,23 @@ + + +int foo(void) +{ + int j = 1; + int i = 6; + + do { + if (i != 6) + i++; + i++; + } while (i != j); + + return j; +} + +/* + * check-name: loop02 pointer + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * + * check-output-ignore + * check-output-excludes: load\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/loop02-pointer.c b/usr/src/tools/smatch/src/validation/mem2reg/loop02-pointer.c new file mode 100644 index 0000000000..fdb0a8fb5f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/loop02-pointer.c @@ -0,0 +1,23 @@ + + +int foo(int *i) +{ + int j = 1; + *i = 6; + + do { + if (*i != 6) + (*i)++; + (*i)++; + } while (*i != j); + + return j; +} + +/* + * check-name: loop02 pointer + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-pattern(0,4): load\\. + * check-output-pattern(1,3): store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/missing-return.c b/usr/src/tools/smatch/src/validation/mem2reg/missing-return.c new file mode 100644 index 0000000000..06f6e4d526 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/missing-return.c @@ -0,0 +1,34 @@ +int f1(void) +{ + if (1) + return 1; +} + +int f0(void) +{ + if (0) + return 0; +} + +int fx(int p) +{ + if (p) + return 0; +} + +int bar(int p) +{ + if (p) + return 0; + p++; +} + +/* + * check-name: missing-return + * check-command: test-linearize -m32 -fdump-ir=mem2reg -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-pattern(1): ret.32 *\\$1 + * check-output-pattern(3): ret.32 *UNDEF + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/quadra00.c b/usr/src/tools/smatch/src/validation/mem2reg/quadra00.c new file mode 100644 index 0000000000..69ddc9772c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/quadra00.c @@ -0,0 +1,27 @@ +#define TEST(N) \ + do { \ + d = b + a[N]; \ + if (d < b) \ + c++; \ + b = d; \ + } while (0) + +int foo(int *a, int b, int c) +{ + int d; + + TEST(0); + TEST(1); + TEST(2); + + return d + c; +} + +/* + * check-name: quadratic phisrc + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * check-output-excludes: phi\\..*, .*, .* + * check-output-excludes: phi\\..*, .*, .*, .* + * check-output-pattern(6): phisrc\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/quadra01.c b/usr/src/tools/smatch/src/validation/mem2reg/quadra01.c new file mode 100644 index 0000000000..b71f46969b --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/quadra01.c @@ -0,0 +1,27 @@ +#include "repeat.h" + +void use(void *, void *, void *, void *); +void *def(void); + +#define BLOCK(n) { \ + void *label; \ + use(&&w##n, &&x##n, &&y##n, &&z##n); \ +w##n: label = def(); goto *label; \ +x##n: label = def(); goto *label; \ +y##n: label = def(); goto *label; \ +z##n: label = def(); goto *label; \ +} + +static void foo(void) { + REPEAT2(5, BLOCK) +} + +/* + * check-name: quadratic @ liveness + * check-command: test-linearize -I. $file + * check-timeout: + * + * check-output-ignore + * check-output-excludes: phi\\. + * check-output-excludes: phisrc\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/quadra02.c b/usr/src/tools/smatch/src/validation/mem2reg/quadra02.c new file mode 100644 index 0000000000..6475c7802c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/quadra02.c @@ -0,0 +1,18 @@ +#include "repeat.h" + +#define PAT(X) int a##X = X; +static void foo(void) +{ + REPEAT2(12, PAT) +} + +/* + * check-name: quadratic vars + * check-command: test-linearize -I. $file + * check-timeout: + * + * check-output-ignore + * check-output-excludes: phi\\. + * check-output-excludes: phisrc\\. + * check-output-excludes: store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/reload-aliasing.c b/usr/src/tools/smatch/src/validation/mem2reg/reload-aliasing.c new file mode 100644 index 0000000000..3aad317bd9 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/reload-aliasing.c @@ -0,0 +1,41 @@ +extern int g, h; + +void f00(int *s) +{ + g = *s; + h = *s; +} + +void f01(int *a, int *b, int *s) +{ + *a = *s; + *b = *s; +} + +/* + * check-name: reload-aliasing.c + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +f00: +.L0: + <entry-point> + load.32 %r2 <- 0[%arg1] + store.32 %r2 -> 0[g] + load.32 %r4 <- 0[%arg1] + store.32 %r4 -> 0[h] + ret + + +f01: +.L2: + <entry-point> + load.32 %r6 <- 0[%arg3] + store.32 %r6 -> 0[%arg1] + load.32 %r9 <- 0[%arg3] + store.32 %r9 -> 0[%arg2] + ret + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/short-load.c b/usr/src/tools/smatch/src/validation/mem2reg/short-load.c new file mode 100644 index 0000000000..c4b4dc4be3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/short-load.c @@ -0,0 +1,29 @@ +#ifdef __SIZEOF_INT__ == 4 +typedef unsigned int u32; +#endif +#ifdef __SIZEOF_SHORT__ == 2 +typedef unsigned short u16; +#endif + + +union u { + u32 a; + u16 b; +}; + +void bar(u16, union u); + +void foo(u16 val) +{ + union u u; + + u.b = val; + bar(u.b, u); +} + +/* + * check-name: short-load + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-contains: load\\.32 + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/store-deadborn.c b/usr/src/tools/smatch/src/validation/mem2reg/store-deadborn.c new file mode 100644 index 0000000000..cca34d592f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/store-deadborn.c @@ -0,0 +1,9 @@ +static void foo(int a) +{ + return; + a = 0; +} + +/* + * check-name: store-deadborn + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/stray-phisrc.c b/usr/src/tools/smatch/src/validation/mem2reg/stray-phisrc.c new file mode 100644 index 0000000000..0da7df7a44 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/stray-phisrc.c @@ -0,0 +1,24 @@ +static int foo(int **g) +{ + int i = 1; + int *a[2]; + int **p; + + a[1] = &i; + if (g) + p = g; + else + p = &a[0]; + p += 1; // will point to a[1] = &i + if (!g) + **p = 0; + return i; +} + +/* + * check-name: stray phisrc + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: phisrc\\. + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/struct.c b/usr/src/tools/smatch/src/validation/mem2reg/struct.c new file mode 100644 index 0000000000..13962a8e6f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/struct.c @@ -0,0 +1,32 @@ +struct s { + int a; + int b; +}; + +int f0(void) +{ + struct s s; + + s.a = 0; + s.b = 1; + + return s.a; +} + +int f1(void) +{ + struct s s; + + s.a = 1; + s.b = 0; + + return s.b; +} + +/* + * check-name: struct + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(2): ret.32 *\\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/undef00.c b/usr/src/tools/smatch/src/validation/mem2reg/undef00.c new file mode 100644 index 0000000000..20ea962ce8 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/undef00.c @@ -0,0 +1,21 @@ +static int badr(void) +{ + int *a; + return *a; +} + +static void badw(int v) +{ + int *a; + *a = v; +} + +/* + * check-name: undef00 + * check-command: test-linearize -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-pattern(1): load\\. + * check-output-pattern(1): load\\..*\\[UNDEF\\] + * check-output-pattern(1): store\\. + * check-output-pattern(1): store\\..*\\[UNDEF\\] + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/undef01.c b/usr/src/tools/smatch/src/validation/mem2reg/undef01.c new file mode 100644 index 0000000000..985c73d6d3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/undef01.c @@ -0,0 +1,16 @@ +static void foo(void) +{ + int *b; + for (;;) + *b++ = 0; +} + +/* + * check-name: undef01 + * check-command: sparse -Wmaybe-uninitialized $file + * check-known-to-fail + * + * check-error-start +crazy04.c:3:13: warning: variable 'b' may be uninitialized + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/unused-var.c b/usr/src/tools/smatch/src/validation/mem2reg/unused-var.c new file mode 100644 index 0000000000..ac39458209 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/unused-var.c @@ -0,0 +1,23 @@ +int foo(int a) +{ + switch (a) { + int u = 1; + + default: + return a; + } +} + +/* + * check-name: unused-var + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + ret.32 %arg1 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/mem2reg/volatile-store00.c b/usr/src/tools/smatch/src/validation/mem2reg/volatile-store00.c new file mode 100644 index 0000000000..d565037ac6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/mem2reg/volatile-store00.c @@ -0,0 +1,27 @@ +void foo(volatile int *p) +{ + *p = 0; + *p = 0; +} + +void bar(void) +{ + extern volatile int i; + i = 0; + i = 0; +} + + +void baz(void) +{ + volatile int i; + i = 0; + i = 0; +} + +/* + * check-name: keep volatile stores + * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file + * check-output-ignore + * check-output-pattern(1,6): store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/memops-volatile.c b/usr/src/tools/smatch/src/validation/memops-volatile.c index 0f3e12ad24..15314e1ce3 100644 --- a/usr/src/tools/smatch/src/validation/memops-volatile.c +++ b/usr/src/tools/smatch/src/validation/memops-volatile.c @@ -1,6 +1,7 @@ static int foo(volatile int *a, int v) { *a = v; + *a = 0; return *a; } @@ -8,14 +9,8 @@ static int foo(volatile int *a, int v) * check-name: memops-volatile * check-command: test-linearize $file * - * check-output-start -foo: -.L0: - <entry-point> - store.32 %arg2 -> 0[%arg1] - load.32 %r5 <- 0[%arg1] - ret.32 %r5 - - - * check-output-end + * check-output-ignore + * check-output-contains: store\\..*%arg2 -> 0\\[%arg1] + * check-output-contains: store\\..*\\$0 -> 0\\[%arg1] + * check-output-contains: load\\..*%r.* <- 0\\[%arg1] */ diff --git a/usr/src/tools/smatch/src/validation/missing-return.c b/usr/src/tools/smatch/src/validation/missing-return.c new file mode 100644 index 0000000000..b6ee75efd6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/missing-return.c @@ -0,0 +1,20 @@ +int foo(int a) +{ +} + +int bar(int a) +{ + if (a) + return 0; +} + +/* + * check-name: missing return + * check-command: sparse -Wno-decl $file + * check-known-to-fail + * + * check-error-start +missing-return.c:3:1: warning: control reaches end of non-void function +missing-return.c:9:1: warning: control reaches end of non-void function + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/multi-input.c b/usr/src/tools/smatch/src/validation/multi-input.c new file mode 100644 index 0000000000..2443d49fda --- /dev/null +++ b/usr/src/tools/smatch/src/validation/multi-input.c @@ -0,0 +1,11 @@ +int a = 1; +int foo(void) {} + +static int b = 1; +static int bar(void) {} + +/* + * check-name: multi-input + * check-command: sparse -Wno-decl $file $file + * check-known-to-fail + */ diff --git a/usr/src/tools/smatch/src/validation/nested-declarator.c b/usr/src/tools/smatch/src/validation/nested-declarator.c index 1efe20ce45..f258d457b3 100644 --- a/usr/src/tools/smatch/src/validation/nested-declarator.c +++ b/usr/src/tools/smatch/src/validation/nested-declarator.c @@ -15,7 +15,7 @@ int i(void (void)(*f)); int j(int [2](*)); /* * check-name: nested declarator vs. parameters - * check-error-start: + * check-error-start nested-declarator.c:11:23: warning: missing identifier in declaration nested-declarator.c:11:23: error: Expected ; at the end of type declaration nested-declarator.c:11:23: error: got ( @@ -25,5 +25,5 @@ nested-declarator.c:14:18: error: Expected ) in function declarator nested-declarator.c:14:18: error: got ( nested-declarator.c:15:14: error: Expected ) in function declarator nested-declarator.c:15:14: error: got ( - * check-error-end: + * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/nested-declarator2.c b/usr/src/tools/smatch/src/validation/nested-declarator2.c index 345a04b069..983b7ef5cc 100644 --- a/usr/src/tools/smatch/src/validation/nested-declarator2.c +++ b/usr/src/tools/smatch/src/validation/nested-declarator2.c @@ -27,7 +27,7 @@ static int (-bad2); static void [2](*bad3); /* * check-name: more on handling of ( in direct-declarator - * check-error-start: + * check-error-start nested-declarator2.c:17:1: warning: non-ANSI definition of function 'w1' nested-declarator2.c:21:21: warning: non-ANSI function declaration of function '<noident>' nested-declarator2.c:22:16: warning: variadic functions must have one named argument @@ -37,5 +37,5 @@ nested-declarator2.c:26:13: error: Expected ) in nested declarator nested-declarator2.c:26:13: error: got - nested-declarator2.c:27:16: error: Expected ; at the end of type declaration nested-declarator2.c:27:16: error: got ( - * check-error-end: + * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/nocast.c b/usr/src/tools/smatch/src/validation/nocast.c index cc0ab6b7c0..6c5da96877 100644 --- a/usr/src/tools/smatch/src/validation/nocast.c +++ b/usr/src/tools/smatch/src/validation/nocast.c @@ -160,7 +160,7 @@ nocast.c:34:33: got unsigned long nocast.c:34:33: warning: implicit cast to nocast type nocast.c:35:39: warning: incorrect type in initializer (different modifiers) nocast.c:35:39: expected unsigned long *static [toplevel] bad_ptr_from -nocast.c:35:39: got unsigned long [nocast] *<noident> +nocast.c:35:39: got unsigned long [nocast] * nocast.c:35:39: warning: implicit cast from nocast type nocast.c:50:16: warning: implicit cast from nocast type nocast.c:54:16: warning: implicit cast from nocast type diff --git a/usr/src/tools/smatch/src/validation/noderef.c b/usr/src/tools/smatch/src/validation/noderef.c index 8c89f6092b..bcd6c08cbe 100644 --- a/usr/src/tools/smatch/src/validation/noderef.c +++ b/usr/src/tools/smatch/src/validation/noderef.c @@ -46,6 +46,6 @@ static void h(void) * check-error-start noderef.c:24:12: warning: incorrect type in assignment (different modifiers) noderef.c:24:12: expected char *[noderef] *q2 -noderef.c:24:12: got char [noderef] **<noident> +noderef.c:24:12: got char [noderef] ** * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/optim/address-used01.c b/usr/src/tools/smatch/src/validation/optim/address-used01.c new file mode 100644 index 0000000000..628923d193 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/address-used01.c @@ -0,0 +1,18 @@ +int foo(int **g, int j) +{ + int i = 1; + int *a; + int **p; + + a = &i; + p = &a; + *p[0] = 0; + return i; +} + +/* + * check-name: address-used01 + * check-command: test-linearize -Wno-decl -fdump-ir=final $file + * check-output-ignore + * check-output-contains: ret\\..* \\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-extend.c b/usr/src/tools/smatch/src/validation/optim/and-extend.c new file mode 100644 index 0000000000..eb58923696 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-extend.c @@ -0,0 +1,27 @@ +typedef unsigned short u16; +typedef short s16; +typedef unsigned int u32; +typedef int s32; + +u32 ufoo(u32 x) +{ + u16 i = ((u16)x) & 0x7fffU; + return i; +} + +u32 sfoo(u32 x) +{ + s16 i = ((s16)x) & 0x7fff; + return i; +} + +/* + * check-name: and-extend + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: trunc\\. + * check-output-excludes: zext\\. + * check-output-excludes: sext\\. + * check-output-contains: and\\.32.*0x7fff + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-extendx.c b/usr/src/tools/smatch/src/validation/optim/and-extendx.c new file mode 100644 index 0000000000..5c181c9338 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-extendx.c @@ -0,0 +1,24 @@ +typedef unsigned short u16; +typedef short s16; +typedef unsigned int u32; +typedef int s32; +typedef unsigned long long u64; +typedef long long s64; + +u64 ufoo(int x) +{ + return x & 0x7fff; +} + +u64 sfoo(int x) +{ + return x & 0x7fff; +} + +/* + * check-name: and-extend + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\.64.*0x7fff + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-lsr.c b/usr/src/tools/smatch/src/validation/optim/and-lsr.c new file mode 100644 index 0000000000..439eb82272 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-lsr.c @@ -0,0 +1,15 @@ +// (x & M) >> S to (x >> S) & (M >> S) + +unsigned int foo(unsigned int x) +{ + return (x & 0xffff) >> 12; +} + +/* + * check-name: and-lsr + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*\\$15 + * check-output-excludes: and\\..*\\$0xffff + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-bf0.c b/usr/src/tools/smatch/src/validation/optim/and-or-bf0.c new file mode 100644 index 0000000000..cfaff4f20d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-bf0.c @@ -0,0 +1,24 @@ +struct s { + int f:3; +}; + +void foo(struct s *p, int a) +{ + p->f = 1; + p->f = a; +} + +void bar(struct s *p, int a) +{ + p->f = a; + p->f = 1; +} + +/* + * check-name: and-or-bf0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(3): and\\. + * check-output-pattern(2): or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-bf1.c b/usr/src/tools/smatch/src/validation/optim/and-or-bf1.c new file mode 100644 index 0000000000..23477ff349 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-bf1.c @@ -0,0 +1,18 @@ +struct s { + int :2; + int f:3; +}; + +void foo(struct s *d, const struct s *s, int a) +{ + d->f = s->f | a; +} + +/* + * check-name: and-or-bf1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(2): and\\. + * check-output-pattern(2): or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-bf2.c b/usr/src/tools/smatch/src/validation/optim/and-or-bf2.c new file mode 100644 index 0000000000..2296da1220 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-bf2.c @@ -0,0 +1,27 @@ +struct s { + char a:3; + char b:3; + char c:2; +}; + +void foo(struct s *p) +{ + p->a = 1; + p->b = 2; + p->c = 3; +} + +/* + * check-name: and-or-bf2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + store.8 $209 -> 0[%arg1] + ret + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-bfs.c b/usr/src/tools/smatch/src/validation/optim/and-or-bfs.c new file mode 100644 index 0000000000..f3f3320471 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-bfs.c @@ -0,0 +1,23 @@ +struct s { + signed int :2; + signed int f:3; +}; + +int bfs(struct s s, int a) +{ + s.f = a; + return s.f; +} + +/* + * check-name: and-or-bfs + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): trunc\\. + * check-output-pattern(1): sext\\. + * check-output-excludes: and\\. + * check-output-excludes: or\\. + * check-output-excludes: shl\\. + * check-output-excludes: lsr\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-bfu.c b/usr/src/tools/smatch/src/validation/optim/and-or-bfu.c new file mode 100644 index 0000000000..b6a080bd1a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-bfu.c @@ -0,0 +1,21 @@ +struct u { + unsigned int :2; + unsigned int f:3; +}; + +int bfu(struct u s, int a) +{ + s.f = a; + return s.f; +} + +/* + * check-name: and-or-bfu + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-excludes: or\\. + * check-output-excludes: shl\\. + * check-output-excludes: lsr\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-bfx.c b/usr/src/tools/smatch/src/validation/optim/and-or-bfx.c new file mode 100644 index 0000000000..57a54cf5ef --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-bfx.c @@ -0,0 +1,18 @@ +struct s { + int f:3; +}; + +void foo(struct s *p, int a, int b) +{ + p->f = a; + p->f = b; +} + +/* + * check-name: and-or-bfx + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(2): and\\. + * check-output-pattern(1): or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-constant0.c b/usr/src/tools/smatch/src/validation/optim/and-or-constant0.c new file mode 100644 index 0000000000..dcf440f33d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-constant0.c @@ -0,0 +1,12 @@ +int foo(int x) +{ + return (x | 0xfffff000) & 0xfff; +} + +/* + * check-name: and-or-constant0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-constant1.c b/usr/src/tools/smatch/src/validation/optim/and-or-constant1.c new file mode 100644 index 0000000000..49823d5ca3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-constant1.c @@ -0,0 +1,14 @@ +int foo(int x) +{ + return (x | 0x000fffff) & 0xfff; +} + +/* + * check-name: or-and-constant1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0xfff + * check-output-excludes: and\\. + * check-output-excludes: or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-constant2.c b/usr/src/tools/smatch/src/validation/optim/and-or-constant2.c new file mode 100644 index 0000000000..d7e66f9c05 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-constant2.c @@ -0,0 +1,13 @@ +int foo(int x) +{ + return (x | 0xfffffff0) & 0xfff; +} + +/* + * check-name: and-or-constant2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: or\\..*\\$0xff0 + * check-output-excludes: or\\..*\\$0xfffffff0 + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-crash.c b/usr/src/tools/smatch/src/validation/optim/and-or-crash.c new file mode 100644 index 0000000000..98a8a9b8f3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-crash.c @@ -0,0 +1,5 @@ +static unsigned a(unsigned b, unsigned c) { (c << 1 | b & 1 << 1) >> 1; } + +/* + * check-name: catch crashes during AND-OR simplifications + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-lsr0.c b/usr/src/tools/smatch/src/validation/optim/and-or-lsr0.c new file mode 100644 index 0000000000..227c5aede2 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-lsr0.c @@ -0,0 +1,13 @@ +int foo(int a, int b) +{ + return ((a & 0x00000fff) | b) >> 12; +} + +/* + * check-name: and-or-lsr0 + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-excludes: or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-lsr1.c b/usr/src/tools/smatch/src/validation/optim/and-or-lsr1.c new file mode 100644 index 0000000000..bd1dbc8aa5 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-lsr1.c @@ -0,0 +1,13 @@ +int foo(int a, int b) +{ + return ((a & 0xfffff000) | b) >> 12; +} + +/* + * check-name: and-or-lsr1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(0): and\\. + * check-output-pattern(1): or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-lsr2.c b/usr/src/tools/smatch/src/validation/optim/and-or-lsr2.c new file mode 100644 index 0000000000..d1af0135d5 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-lsr2.c @@ -0,0 +1,13 @@ +int foo(int x, int y) +{ + return ((x & 0xf0ffffff) | y) >> 12; +} + +/* + * check-name: and-or-lsr2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*\\$0xf0fff + * check-output-excludes: and\\..*\\$0xf0ffffff + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-lsrx.c b/usr/src/tools/smatch/src/validation/optim/and-or-lsrx.c new file mode 100644 index 0000000000..31adca92e8 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-lsrx.c @@ -0,0 +1,13 @@ +unsigned int foo(unsigned int x, unsigned int y, unsigned int a) +{ + return ((x & y) | (a & 0x0fff)) >> 12; +} + +/* + * check-name: and-or-lsrx + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-excludes: or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-mask.c b/usr/src/tools/smatch/src/validation/optim/and-or-mask.c new file mode 100644 index 0000000000..4680378955 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-mask.c @@ -0,0 +1,18 @@ +int foo(int a, int b) +{ + return ((a & 7) | (b & 3)) & 8; +} + +/* + * check-name: and-or-mask + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + ret.32 $0 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-mask0.c b/usr/src/tools/smatch/src/validation/optim/and-or-mask0.c new file mode 100644 index 0000000000..2d2245ea5c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-mask0.c @@ -0,0 +1,12 @@ +int foo(int a, int b) +{ + return ((a & 0xfffff000) | b) & 0xfff; +} + +/* + * check-name: and-or-mask0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-mask1.c b/usr/src/tools/smatch/src/validation/optim/and-or-mask1.c new file mode 100644 index 0000000000..bff3a89f23 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-mask1.c @@ -0,0 +1,13 @@ +int foo(int a, int b) +{ + return ((a & 0x0fffffff) | b) & 0xfff; +} + +/* + * check-name: and-or-mask1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-pattern(1): or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-mask2.c b/usr/src/tools/smatch/src/validation/optim/and-or-mask2.c new file mode 100644 index 0000000000..cab802a664 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-mask2.c @@ -0,0 +1,13 @@ +int foo(int x, int y) +{ + return ((x & 0xffffff0f) | y) & 0xfff; +} + +/* + * check-name: and-or-mask2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*\\$0xf0f + * check-output-excludes: and\\..*\\$0xffffff0f + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-mask3s.c b/usr/src/tools/smatch/src/validation/optim/and-or-mask3s.c new file mode 100644 index 0000000000..cf26472336 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-mask3s.c @@ -0,0 +1,25 @@ +#define W 3 +#define S 8 +#define M (W << S) + +static inline int fun(unsigned int x, unsigned int y) +{ + return ((x & M) | (y << S)) >> S; +} + +short foo(unsigned int x, unsigned int y) +{ + return fun(x, y) & W; +} + +/* + * check-name: and-or-mask3s + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-pattern(1): lsr\\. + * check-output-pattern(1): or\\. + * check-output-pattern(1): and\\. + * check-output-excludes: shl\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-mask3u.c b/usr/src/tools/smatch/src/validation/optim/and-or-mask3u.c new file mode 100644 index 0000000000..c5b6c5fa02 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-mask3u.c @@ -0,0 +1,25 @@ +#define W 3 +#define S 8 +#define M (W << S) + +static inline int fun(unsigned int x, unsigned int y) +{ + return ((x & M) | (y << S)) >> S; +} + +int foo(unsigned int x, unsigned int y) +{ + return fun(x, y) & W; +} + +/* + * check-name: and-or-mask3u + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-pattern(1): lsr\\. + * check-output-pattern(1): or\\. + * check-output-pattern(1): and\\. + * check-output-excludes: shl\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-mask4.c b/usr/src/tools/smatch/src/validation/optim/and-or-mask4.c new file mode 100644 index 0000000000..1c4bc01a89 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-mask4.c @@ -0,0 +1,25 @@ +#define W 3 +#define S 8 +#define M (W << S) + +static inline int fun(unsigned int x, unsigned int y) +{ + return ((x & W) | (y >> S)) << S; +} + +int foo(unsigned int x, unsigned int y) +{ + return fun(x, y) & M; +} + +/* + * check-name: and-or-mask4 + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-pattern(1): shl\\. + * check-output-pattern(1): or\\. + * check-output-pattern(1): and\\. + * check-output-excludes: lsr\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-maskx.c b/usr/src/tools/smatch/src/validation/optim/and-or-maskx.c new file mode 100644 index 0000000000..21d44e8d0e --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-maskx.c @@ -0,0 +1,13 @@ +int foo(int x, int y, int a) +{ + return ((x & y) | (a & 0xf000)) & 0x0fff; +} + +/* + * check-name: and-or-maskx + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(2): and\\. + * check-output-excludes: or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-shl0.c b/usr/src/tools/smatch/src/validation/optim/and-or-shl0.c new file mode 100644 index 0000000000..4850b32621 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-shl0.c @@ -0,0 +1,12 @@ +int foo(int a, int b) +{ + return ((a & 0xfff00000) | b) << 12; +} + +/* + * check-name: and-or-shl0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-shl1.c b/usr/src/tools/smatch/src/validation/optim/and-or-shl1.c new file mode 100644 index 0000000000..bea2224503 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-shl1.c @@ -0,0 +1,13 @@ +int foo(int a, int b) +{ + return ((a & 0x000fffff) | b) << 12; +} + +/* + * check-name: and-or-shl1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(0): and\\. + * check-output-pattern(1): or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-shl2.c b/usr/src/tools/smatch/src/validation/optim/and-or-shl2.c new file mode 100644 index 0000000000..f5f62758df --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-shl2.c @@ -0,0 +1,13 @@ +int foo(int x, int y) +{ + return ((x & 0xffffff0f) | y) << 12; +} + +/* + * check-name: and-or-shl2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*\\$0xfff0f + * check-output-excludes: and\\..*\\$0xffffff0f + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-shlx.c b/usr/src/tools/smatch/src/validation/optim/and-or-shlx.c new file mode 100644 index 0000000000..ec2a2ced46 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-shlx.c @@ -0,0 +1,13 @@ +unsigned int foo(unsigned int x, unsigned int y, unsigned int a) +{ + return ((x & y) | (a & 0xfff00000)) << 12; +} + +/* + * check-name: and-or-shlx + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-excludes: or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-trunc0.c b/usr/src/tools/smatch/src/validation/optim/and-or-trunc0.c new file mode 100644 index 0000000000..3d326b6afa --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-trunc0.c @@ -0,0 +1,13 @@ +char foo(int x, int y) +{ + return (x & 0xff00) | y; +} + +/* + * check-name: and-or-trunc0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: and\\. + * check-output-excludes: or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-trunc1.c b/usr/src/tools/smatch/src/validation/optim/and-or-trunc1.c new file mode 100644 index 0000000000..6bbe8a8cf6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-trunc1.c @@ -0,0 +1,12 @@ +char foo(int x, int y) +{ + return (x & 0xffff) | y; +} + +/* + * check-name: and-or-trunc1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: and\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-trunc2.c b/usr/src/tools/smatch/src/validation/optim/and-or-trunc2.c new file mode 100644 index 0000000000..e66c1f142a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-trunc2.c @@ -0,0 +1,13 @@ +char foo(int x, int y) +{ + return (x & 0xff07) | y; +} + +/* + * check-name: and-or-trunc2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-pattern(1): and\\..*\\$7 + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-or-truncx.c b/usr/src/tools/smatch/src/validation/optim/and-or-truncx.c new file mode 100644 index 0000000000..ef8249a107 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-or-truncx.c @@ -0,0 +1,13 @@ +char foo(int x, int y, int b) +{ + return (x & y) | (b & 0xff00); +} + +/* + * check-name: and-or-truncx + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-excludes: or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/and-trunc.c b/usr/src/tools/smatch/src/validation/optim/and-trunc.c new file mode 100644 index 0000000000..df1e4d03eb --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/and-trunc.c @@ -0,0 +1,20 @@ +short smask(short x) +{ + return x & (short) 0x7fff; +} + +short umask(unsigned short x) +{ + return x & (unsigned short) 0x7fff; +} + +/* + * check-name: and-trunc + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: sext\\. + * check-output-excludes: zext\\. + * check-output-excludes: trunc\\. + * check-output-contains: and\\.16 + */ diff --git a/usr/src/tools/smatch/src/validation/linear/bitfield-init-zero.c b/usr/src/tools/smatch/src/validation/optim/bitfield-init-zero.c index 39a64345ed..e619d1d25d 100644 --- a/usr/src/tools/smatch/src/validation/linear/bitfield-init-zero.c +++ b/usr/src/tools/smatch/src/validation/optim/bitfield-init-zero.c @@ -57,17 +57,17 @@ int bfs_get0(void) bfuu_init: .L0: <entry-point> - cast.9 %r2 <- (32) %arg1 - shl.32 %r4 <- %r2, $11 - ret.32 %r4 + and.32 %r4 <- %arg1, $511 + shl.32 %r5 <- %r4, $11 + ret.32 %r5 bfus_init: .L2: <entry-point> - scast.9 %r10 <- (32) %arg1 - shl.32 %r12 <- %r10, $11 - ret.32 %r12 + and.32 %r13 <- %arg1, $511 + shl.32 %r14 <- %r13, $11 + ret.32 %r14 bfu_get0: @@ -79,17 +79,17 @@ bfu_get0: bfsu_init: .L6: <entry-point> - cast.9 %r23 <- (32) %arg1 - shl.32 %r25 <- %r23, $11 - ret.32 %r25 + and.32 %r27 <- %arg1, $511 + shl.32 %r28 <- %r27, $11 + ret.32 %r28 bfss_init: .L8: <entry-point> - scast.9 %r31 <- (32) %arg1 - shl.32 %r33 <- %r31, $11 - ret.32 %r33 + and.32 %r36 <- %arg1, $511 + shl.32 %r37 <- %r36, $11 + ret.32 %r37 bfs_get0: diff --git a/usr/src/tools/smatch/src/validation/bitfield-size.c b/usr/src/tools/smatch/src/validation/optim/bitfield-size.c index ce78ecf217..0d2deeeac2 100644 --- a/usr/src/tools/smatch/src/validation/bitfield-size.c +++ b/usr/src/tools/smatch/src/validation/optim/bitfield-size.c @@ -35,7 +35,10 @@ unsigned int get_pbfi_b(struct bfi *bf) { return bf->b; } * check-command: test-linearize -Wno-decl $file * check-output-ignore * - * check-output-pattern-24-times: cast\\. - * check-output-pattern-12-times: cast\\.4 - * check-output-pattern-6-times: lsr\\..*\\$6 + * check-output-excludes: and\\..*\\$960 + * check-output-excludes: zext\\. + * check-output-pattern(8): and\\..*\\$15 + * check-output-pattern(4): sext\\. + * check-output-pattern(4): trunc\\.4 + * check-output-pattern(6): lsr\\..*\\$6 */ diff --git a/usr/src/tools/smatch/src/validation/optim/bitfield-store-load0.c b/usr/src/tools/smatch/src/validation/optim/bitfield-store-load0.c new file mode 100644 index 0000000000..f68cb60004 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bitfield-store-load0.c @@ -0,0 +1,44 @@ +int ufoo(unsigned int a) +{ + struct u { + unsigned int :2; + unsigned int a:3; + } bf; + + bf.a = a; + return bf.a; +} + +int sfoo(int a) +{ + struct s { + signed int :2; + signed int a:3; + } bf; + + bf.a = a; + return bf.a; +} + +/* + * check-name: optim store/load bitfields + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +ufoo: +.L0: + <entry-point> + and.32 %r11 <- %arg1, $7 + ret.32 %r11 + + +sfoo: +.L2: + <entry-point> + trunc.3 %r16 <- (32) %arg1 + sext.32 %r23 <- (3) %r16 + ret.32 %r23 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/bitfield-store-loads.c b/usr/src/tools/smatch/src/validation/optim/bitfield-store-loads.c new file mode 100644 index 0000000000..dc625131e1 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bitfield-store-loads.c @@ -0,0 +1,23 @@ +struct s { + char :2; + char f:3; +}; + +int foo(struct s s, int a) +{ + s.f = a; + return s.f; +} + +/* + * check-name: bitfield-store-load signed + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: shl\\. + * check-output-excludes: lsr\\. + * check-output-excludes: or\\. + * check-output-excludes: [sz]ext\\. + * check-output-excludes: trunc\\. + * check-output-pattern(1): and\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/bitfield-store-loadu.c b/usr/src/tools/smatch/src/validation/optim/bitfield-store-loadu.c new file mode 100644 index 0000000000..7fa1593d91 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bitfield-store-loadu.c @@ -0,0 +1,21 @@ +struct s { + unsigned int :2; + unsigned int f:3; +}; + +int foo(struct s s, int a) +{ + s.f = a; + return s.f; +} + +/* + * check-name: bitfield-store-load unsigned + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: shl\\. + * check-output-excludes: lsr\\. + * check-output-excludes: or\\. + * check-output-pattern(1): and\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/bits-not-zero.c b/usr/src/tools/smatch/src/validation/optim/bits-not-zero.c new file mode 100644 index 0000000000..189fe3311d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bits-not-zero.c @@ -0,0 +1,30 @@ +int or_not0(int a) { return a | ~0; } +int and_not0(int a) { return a & ~0; } +int xor_not0(int a) { return a ^ ~0; } + +/* + * check-name: bool-not-zero + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +or_not0: +.L0: + <entry-point> + ret.32 $0xffffffff + + +and_not0: +.L2: + <entry-point> + ret.32 %arg1 + + +xor_not0: +.L4: + <entry-point> + not.32 %r8 <- %arg1 + ret.32 %r8 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/bool-context-fp.c b/usr/src/tools/smatch/src/validation/optim/bool-context-fp.c new file mode 100644 index 0000000000..c41370ba4a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bool-context-fp.c @@ -0,0 +1,93 @@ +#define bool _Bool + +bool bfimp(float a) { return a; } +bool bfexp(float a) { return (bool)a; } + +bool bfnot(float a) { return !a; } +int ifnot(float a) { return !a; } +bool bfior(float a, float b) { return a || b; } +int ifior(float a, float b) { return a || b; } +bool bfand(float a, float b) { return a && b; } +int ifand(float a, float b) { return a && b; } + +/* + * check-name: bool context fp + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +bfimp: +.L0: + <entry-point> + setfval.32 %r2 <- 0.000000e+00 + fcmpune.1 %r3 <- %arg1, %r2 + ret.1 %r3 + + +bfexp: +.L2: + <entry-point> + setfval.32 %r6 <- 0.000000e+00 + fcmpune.1 %r7 <- %arg1, %r6 + ret.1 %r7 + + +bfnot: +.L4: + <entry-point> + setfval.32 %r10 <- 0.000000e+00 + fcmpoeq.1 %r12 <- %arg1, %r10 + ret.1 %r12 + + +ifnot: +.L6: + <entry-point> + setfval.32 %r15 <- 0.000000e+00 + fcmpoeq.32 %r16 <- %arg1, %r15 + ret.32 %r16 + + +bfior: +.L8: + <entry-point> + setfval.32 %r19 <- 0.000000e+00 + fcmpune.1 %r20 <- %arg1, %r19 + fcmpune.1 %r23 <- %arg2, %r19 + or.1 %r24 <- %r20, %r23 + ret.1 %r24 + + +ifior: +.L10: + <entry-point> + setfval.32 %r29 <- 0.000000e+00 + fcmpune.1 %r30 <- %arg1, %r29 + fcmpune.1 %r33 <- %arg2, %r29 + or.1 %r34 <- %r30, %r33 + zext.32 %r35 <- (1) %r34 + ret.32 %r35 + + +bfand: +.L12: + <entry-point> + setfval.32 %r38 <- 0.000000e+00 + fcmpune.1 %r39 <- %arg1, %r38 + fcmpune.1 %r42 <- %arg2, %r38 + and.1 %r43 <- %r39, %r42 + ret.1 %r43 + + +ifand: +.L14: + <entry-point> + setfval.32 %r48 <- 0.000000e+00 + fcmpune.1 %r49 <- %arg1, %r48 + fcmpune.1 %r52 <- %arg2, %r48 + and.1 %r53 <- %r49, %r52 + zext.32 %r54 <- (1) %r53 + ret.32 %r54 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/bool-context.c b/usr/src/tools/smatch/src/validation/optim/bool-context.c index 11326d391f..505fa5cee6 100644 --- a/usr/src/tools/smatch/src/validation/optim/bool-context.c +++ b/usr/src/tools/smatch/src/validation/optim/bool-context.c @@ -8,5 +8,5 @@ bool bool_and(int a, int b) { return a && b; } * check-command: test-linearize -Wno-decl $file * check-output-ignore * - * check-output-pattern-4-times: setne\\..* %arg[12] + * check-output-pattern(4): setne\\..* %arg[12] */ diff --git a/usr/src/tools/smatch/src/validation/optim/bool-eq0.c b/usr/src/tools/smatch/src/validation/optim/bool-eq0.c new file mode 100644 index 0000000000..dbc93d95c6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bool-eq0.c @@ -0,0 +1,12 @@ +int beq0(int a) { return a == 0; } +int bnotne0(int a) { return !(a != 0); } +int bnot(int a) { return !a; } + +/* + * check-name: bool-eq0 + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * + * check-output-excludes: setne\\. + * check-output-contains: seteq\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/bool-int-bool.c b/usr/src/tools/smatch/src/validation/optim/bool-int-bool.c new file mode 100644 index 0000000000..de34a68bb6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bool-int-bool.c @@ -0,0 +1,12 @@ +_Bool beq0(_Bool a) { return (a == 0); } +_Bool beq1(_Bool a) { return (a == 1); } +_Bool bne0(_Bool a) { return (a != 0); } +_Bool bne1(_Bool a) { return (a != 1); } + +/* + * check-name: bool - int - bool constants + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: cast\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/bool-ne0.c b/usr/src/tools/smatch/src/validation/optim/bool-ne0.c new file mode 100644 index 0000000000..b8206f6c79 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bool-ne0.c @@ -0,0 +1,12 @@ +int bne0(int a) { return a != 0; } +int bnoteq0(int a) { return !(a == 0); } +int bnotnot(int a) { return !(!a); } + +/* + * check-name: bool-ne0 + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * + * check-output-excludes: seteq\\. + * check-output-contains: setne\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/bool-neq0.c b/usr/src/tools/smatch/src/validation/optim/bool-neq0.c new file mode 100644 index 0000000000..d6ad7aec3d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bool-neq0.c @@ -0,0 +1,12 @@ +int bneq0(int a) { return a != 0; } +int bnoteq0(int a) { return !(a == 0); } +int bnotnot(int a) { return !(!a); } + +/* + * check-name: bool-neq0 + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * + * check-output-excludes: seteq\\. + * check-output-contains: setne\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/bool-sext-test.c b/usr/src/tools/smatch/src/validation/optim/bool-sext-test.c new file mode 100644 index 0000000000..bd85e06ee8 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bool-sext-test.c @@ -0,0 +1,12 @@ +_Bool eqs0( signed char a) { return a == 0; } +_Bool eqs1( signed char a) { return a == 1; } +_Bool nes0( signed char a) { return a != 0; } +_Bool nes1( signed char a) { return a != 1; } + +/* + * check-name: bool-sext-test + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: sext\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/bool-simplify.c b/usr/src/tools/smatch/src/validation/optim/bool-simplify.c index 05be114978..fe8ce88b06 100644 --- a/usr/src/tools/smatch/src/validation/optim/bool-simplify.c +++ b/usr/src/tools/smatch/src/validation/optim/bool-simplify.c @@ -18,6 +18,17 @@ int or_1(int a) return a || 1; } +// try again but with something true but != 1 +int and_2(int a) +{ + return a && 2; +} + +int or_2(int a) +{ + return a || 2; +} + /* * check-name: bool-simplify * check-command: test-linearize -Wno-decl $file @@ -32,17 +43,15 @@ and_0: and_1: .L2: <entry-point> - setne.1 %r8 <- %arg1, $0 - cast.32 %r11 <- (1) %r8 - ret.32 %r11 + setne.32 %r9 <- %arg1, $0 + ret.32 %r9 or_0: .L4: <entry-point> - setne.1 %r14 <- %arg1, $0 - cast.32 %r17 <- (1) %r14 - ret.32 %r17 + setne.32 %r14 <- %arg1, $0 + ret.32 %r14 or_1: @@ -51,5 +60,18 @@ or_1: ret.32 $1 +and_2: +.L8: + <entry-point> + setne.32 %r25 <- %arg1, $0 + ret.32 %r25 + + +or_2: +.L10: + <entry-point> + ret.32 $1 + + * check-output-end */ diff --git a/usr/src/tools/smatch/src/validation/optim/bool-simplify2.c b/usr/src/tools/smatch/src/validation/optim/bool-simplify2.c new file mode 100644 index 0000000000..a089fe6253 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bool-simplify2.c @@ -0,0 +1,215 @@ +typedef unsigned int uint; +typedef _Bool bool; + +static uint ini(uint a) { return !a; } +static bool bni(uint a) { return !a; } +static uint ioii(uint a, uint b) { return a || b; } +static uint iaii(uint a, uint b) { return a && b; } +static bool boii(uint a, uint b) { return a || b; } +static bool baii(uint a, uint b) { return a && b; } +static uint ioiii(uint a, uint b, uint c) { return a || b || c; } +static uint iaiii(uint a, uint b, uint c) { return a && b && c; } +static bool boiii(uint a, uint b, uint c) { return a || b || c; } +static bool baiii(uint a, uint b, uint c) { return a && b && c; } + +static uint inb(bool a) { return !a; } +static bool bnb(bool a) { return !a; } +static uint iobb(bool a, bool b) { return a || b; } +static uint iabb(bool a, bool b) { return a && b; } +static bool bobb(bool a, bool b) { return a || b; } +static bool babb(bool a, bool b) { return a && b; } +static uint iobbb(bool a, bool b, bool c) { return a || b || c; } +static uint iabbb(bool a, bool b, bool c) { return a && b && c; } +static bool bobbb(bool a, bool b, bool c) { return a || b || c; } +static bool babbb(bool a, bool b, bool c) { return a && b && c; } + +/* + * check-name: bool-simplify2 + * check-command: test-linearize $file + * + * check-output-pattern(20): setne\\. + * check-output-pattern(4): seteq\\. + * check-output-pattern(8): zext\\. + * check-output-pattern(12): and + * check-output-pattern(12): or + * check-output-end + * + * check-output-start +ini: +.L0: + <entry-point> + seteq.32 %r2 <- %arg1, $0 + ret.32 %r2 + + +bni: +.L2: + <entry-point> + seteq.1 %r6 <- %arg1, $0 + ret.1 %r6 + + +ioii: +.L4: + <entry-point> + setne.1 %r9 <- %arg1, $0 + setne.1 %r11 <- %arg2, $0 + or.1 %r12 <- %r9, %r11 + zext.32 %r13 <- (1) %r12 + ret.32 %r13 + + +iaii: +.L6: + <entry-point> + setne.1 %r16 <- %arg1, $0 + setne.1 %r18 <- %arg2, $0 + and.1 %r19 <- %r16, %r18 + zext.32 %r20 <- (1) %r19 + ret.32 %r20 + + +boii: +.L8: + <entry-point> + setne.1 %r23 <- %arg1, $0 + setne.1 %r25 <- %arg2, $0 + or.1 %r26 <- %r23, %r25 + ret.1 %r26 + + +baii: +.L10: + <entry-point> + setne.1 %r31 <- %arg1, $0 + setne.1 %r33 <- %arg2, $0 + and.1 %r34 <- %r31, %r33 + ret.1 %r34 + + +ioiii: +.L12: + <entry-point> + setne.1 %r39 <- %arg1, $0 + setne.1 %r41 <- %arg2, $0 + or.1 %r42 <- %r39, %r41 + setne.1 %r46 <- %arg3, $0 + or.1 %r47 <- %r42, %r46 + zext.32 %r48 <- (1) %r47 + ret.32 %r48 + + +iaiii: +.L14: + <entry-point> + setne.1 %r51 <- %arg1, $0 + setne.1 %r53 <- %arg2, $0 + and.1 %r54 <- %r51, %r53 + setne.1 %r58 <- %arg3, $0 + and.1 %r59 <- %r54, %r58 + zext.32 %r60 <- (1) %r59 + ret.32 %r60 + + +boiii: +.L16: + <entry-point> + setne.1 %r63 <- %arg1, $0 + setne.1 %r65 <- %arg2, $0 + or.1 %r66 <- %r63, %r65 + setne.1 %r70 <- %arg3, $0 + or.1 %r71 <- %r66, %r70 + ret.1 %r71 + + +baiii: +.L18: + <entry-point> + setne.1 %r76 <- %arg1, $0 + setne.1 %r78 <- %arg2, $0 + and.1 %r79 <- %r76, %r78 + setne.1 %r83 <- %arg3, $0 + and.1 %r84 <- %r79, %r83 + ret.1 %r84 + + +inb: +.L20: + <entry-point> + seteq.32 %r89 <- %arg1, $0 + ret.32 %r89 + + +bnb: +.L22: + <entry-point> + seteq.1 %r93 <- %arg1, $0 + ret.1 %r93 + + +iobb: +.L24: + <entry-point> + or.1 %r97 <- %arg1, %arg2 + zext.32 %r98 <- (1) %r97 + ret.32 %r98 + + +iabb: +.L26: + <entry-point> + and.1 %r102 <- %arg1, %arg2 + zext.32 %r103 <- (1) %r102 + ret.32 %r103 + + +bobb: +.L28: + <entry-point> + or.1 %r107 <- %arg1, %arg2 + ret.1 %r107 + + +babb: +.L30: + <entry-point> + and.1 %r113 <- %arg1, %arg2 + ret.1 %r113 + + +iobbb: +.L32: + <entry-point> + or.1 %r119 <- %arg1, %arg2 + or.1 %r123 <- %r119, %arg3 + zext.32 %r124 <- (1) %r123 + ret.32 %r124 + + +iabbb: +.L34: + <entry-point> + and.1 %r128 <- %arg1, %arg2 + and.1 %r132 <- %r128, %arg3 + zext.32 %r133 <- (1) %r132 + ret.32 %r133 + + +bobbb: +.L36: + <entry-point> + or.1 %r137 <- %arg1, %arg2 + or.1 %r141 <- %r137, %arg3 + ret.1 %r141 + + +babbb: +.L38: + <entry-point> + and.1 %r147 <- %arg1, %arg2 + and.1 %r151 <- %r147, %arg3 + ret.1 %r151 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/bool-zext-test.c b/usr/src/tools/smatch/src/validation/optim/bool-zext-test.c new file mode 100644 index 0000000000..138938b0a5 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/bool-zext-test.c @@ -0,0 +1,12 @@ +_Bool equ0(unsigned char a) { return a == 0; } +_Bool equ1(unsigned char a) { return a == 1; } +_Bool neu0(unsigned char a) { return a != 0; } +_Bool neu1(unsigned char a) { return a != 1; } + +/* + * check-name: bool-zext-test + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: zext\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/call-complex-pointer.c b/usr/src/tools/smatch/src/validation/optim/call-complex-pointer.c new file mode 100644 index 0000000000..d1b68fb161 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/call-complex-pointer.c @@ -0,0 +1,13 @@ +int foo(int p, int (*f0)(int), int (*f1)(int), int arg) +{ + return (p ? f0 : f1)(arg); +} +/* + * check-name: call-complex-pointer + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-excludes: ptrcast\\. + * check-output-contains: select\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/call-inlined.c b/usr/src/tools/smatch/src/validation/optim/call-inlined.c new file mode 100644 index 0000000000..f21b32949c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/call-inlined.c @@ -0,0 +1,30 @@ +static const char messg[] = "def"; + +static inline int add(int a, int b) +{ + return a + b; +} + +int foo(int a, int b, int p) +{ + if (p) { + add(a + b, 1); + return p; + } + return 0; +} + +/* + * check-name: call-inlined + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + select.32 %r10 <- %arg3, %arg3, $0 + ret.32 %r10 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/canonical-add.c b/usr/src/tools/smatch/src/validation/optim/canonical-add.c new file mode 100644 index 0000000000..6f32a61e20 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/canonical-add.c @@ -0,0 +1,55 @@ +int xpc_add_ypc(int x, int y) +{ + return (x + 1) + (y + 1); +} + +int xmc_add_ypc(int x, int y) +{ + return (x - 1) + (y + 1); +} + +int xpc_add_ymc(int x, int y) +{ + return (x + 1) + (y - 1); +} + +int xmc_add_ymc(int x, int y) +{ + return (x - 1) + (y - 1); +} + +int xpc_sub_ypc(int x, int y) +{ + return (x + 1) - (y + 1); +} + +int xmc_sub_ypc(int x, int y) +{ + return (x - 1) - (y + 1); +} + +int xpc_sub_ymc(int x, int y) +{ + return (x + 1) - (y - 1); +} + +int xmc_sub_ymc(int x, int y) +{ + return (x - 1) - (y - 1); +} + +/* + * check-name: canonical-add + * check-description: + * 1) verify that constants in add/sub chains are + * pushed at the right of the whole chain. + * For example '(a + 1) + b' must be canonicalized into '(a + b) + 1' + * This is needed for '(a + 1) + b - 1' to be simplified into '(a + b)' + * + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * check-output-ignore + + * check-output-excludes: \\$1 + * check-output-excludes: \\$-1 + */ diff --git a/usr/src/tools/smatch/src/validation/optim/canonical-cmp.c b/usr/src/tools/smatch/src/validation/optim/canonical-cmp.c new file mode 100644 index 0000000000..e0ca7db363 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/canonical-cmp.c @@ -0,0 +1,124 @@ +typedef signed int sint; +typedef unsigned int uint; + +sint seq(sint p, sint a) { return (123 == p) ? a : 0; } +sint sne(sint p, sint a) { return (123 != p) ? a : 0; } + +sint slt(sint p, sint a) { return (123 > p) ? a : 0; } +sint sle(sint p, sint a) { return (123 >= p) ? a : 0; } +sint sge(sint p, sint a) { return (123 <= p) ? a : 0; } +sint sgt(sint p, sint a) { return (123 < p) ? a : 0; } + +uint ueq(uint p, uint a) { return (123 == p) ? a : 0; } +uint une(uint p, uint a) { return (123 != p) ? a : 0; } + +uint ubt(uint p, uint a) { return (123 > p) ? a : 0; } +uint ube(uint p, uint a) { return (123 >= p) ? a : 0; } +uint uae(uint p, uint a) { return (123 <= p) ? a : 0; } +uint uat(uint p, uint a) { return (123 < p) ? a : 0; } + +/* + * check-name: canonical-cmp + * check-command: test-linearize -Wno-decl $file + * + * check-output-excludes: \\$123, + * + * check-output-start +seq: +.L0: + <entry-point> + seteq.32 %r3 <- %arg1, $123 + select.32 %r4 <- %r3, %arg2, $0 + ret.32 %r4 + + +sne: +.L2: + <entry-point> + setne.32 %r8 <- %arg1, $123 + select.32 %r9 <- %r8, %arg2, $0 + ret.32 %r9 + + +slt: +.L4: + <entry-point> + setlt.32 %r13 <- %arg1, $123 + select.32 %r14 <- %r13, %arg2, $0 + ret.32 %r14 + + +sle: +.L6: + <entry-point> + setle.32 %r18 <- %arg1, $123 + select.32 %r19 <- %r18, %arg2, $0 + ret.32 %r19 + + +sge: +.L8: + <entry-point> + setge.32 %r23 <- %arg1, $123 + select.32 %r24 <- %r23, %arg2, $0 + ret.32 %r24 + + +sgt: +.L10: + <entry-point> + setgt.32 %r28 <- %arg1, $123 + select.32 %r29 <- %r28, %arg2, $0 + ret.32 %r29 + + +ueq: +.L12: + <entry-point> + seteq.32 %r33 <- %arg1, $123 + select.32 %r34 <- %r33, %arg2, $0 + ret.32 %r34 + + +une: +.L14: + <entry-point> + setne.32 %r38 <- %arg1, $123 + select.32 %r39 <- %r38, %arg2, $0 + ret.32 %r39 + + +ubt: +.L16: + <entry-point> + setb.32 %r43 <- %arg1, $123 + select.32 %r44 <- %r43, %arg2, $0 + ret.32 %r44 + + +ube: +.L18: + <entry-point> + setbe.32 %r48 <- %arg1, $123 + select.32 %r49 <- %r48, %arg2, $0 + ret.32 %r49 + + +uae: +.L20: + <entry-point> + setae.32 %r53 <- %arg1, $123 + select.32 %r54 <- %r53, %arg2, $0 + ret.32 %r54 + + +uat: +.L22: + <entry-point> + seta.32 %r58 <- %arg1, $123 + select.32 %r59 <- %r58, %arg2, $0 + ret.32 %r59 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/canonical-fcmp.c b/usr/src/tools/smatch/src/validation/optim/canonical-fcmp.c new file mode 100644 index 0000000000..b919a55f73 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/canonical-fcmp.c @@ -0,0 +1,123 @@ +extern double g; + +int fcmp_eq(double a) { return (g == a); } +int fcmp_ne(double a) { return (g != a); } + +int fcmp_gt(double a) { return (g > a); } +int fcmp_ge(double a) { return (g >= a); } +int fcmp_le(double a) { return (g <= a); } +int fcmp_lt(double a) { return (g < a); } + +int nfcmp_ne(double a) { return !(g == a); } +int nfcmp_eq(double a) { return !(g != a); } + +int nfcmp_le(double a) { return !(g > a); } +int nfcmp_lt(double a) { return !(g >= a); } +int nfcmp_gt(double a) { return !(g <= a); } +int nfcmp_ge(double a) { return !(g < a); } + +/* + * check-name: canonical-cmp + * check-command: test-linearize -Wno-decl $file + * + * check-output-excludes: \\$123, + * + * check-output-start +fcmp_eq: +.L0: + <entry-point> + load.64 %r1 <- 0[g] + fcmpoeq.32 %r3 <- %r1, %arg1 + ret.32 %r3 + + +fcmp_ne: +.L2: + <entry-point> + load.64 %r5 <- 0[g] + fcmpune.32 %r7 <- %r5, %arg1 + ret.32 %r7 + + +fcmp_gt: +.L4: + <entry-point> + load.64 %r9 <- 0[g] + fcmpogt.32 %r11 <- %r9, %arg1 + ret.32 %r11 + + +fcmp_ge: +.L6: + <entry-point> + load.64 %r13 <- 0[g] + fcmpoge.32 %r15 <- %r13, %arg1 + ret.32 %r15 + + +fcmp_le: +.L8: + <entry-point> + load.64 %r17 <- 0[g] + fcmpole.32 %r19 <- %r17, %arg1 + ret.32 %r19 + + +fcmp_lt: +.L10: + <entry-point> + load.64 %r21 <- 0[g] + fcmpolt.32 %r23 <- %r21, %arg1 + ret.32 %r23 + + +nfcmp_ne: +.L12: + <entry-point> + load.64 %r25 <- 0[g] + fcmpune.32 %r28 <- %r25, %arg1 + ret.32 %r28 + + +nfcmp_eq: +.L14: + <entry-point> + load.64 %r30 <- 0[g] + fcmpoeq.32 %r33 <- %r30, %arg1 + ret.32 %r33 + + +nfcmp_le: +.L16: + <entry-point> + load.64 %r35 <- 0[g] + fcmpule.32 %r38 <- %r35, %arg1 + ret.32 %r38 + + +nfcmp_lt: +.L18: + <entry-point> + load.64 %r40 <- 0[g] + fcmpult.32 %r43 <- %r40, %arg1 + ret.32 %r43 + + +nfcmp_gt: +.L20: + <entry-point> + load.64 %r45 <- 0[g] + fcmpugt.32 %r48 <- %r45, %arg1 + ret.32 %r48 + + +nfcmp_ge: +.L22: + <entry-point> + load.64 %r50 <- 0[g] + fcmpuge.32 %r53 <- %r50, %arg1 + ret.32 %r53 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/canonical-mul.c b/usr/src/tools/smatch/src/validation/optim/canonical-mul.c new file mode 100644 index 0000000000..3ae9e3a64c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/canonical-mul.c @@ -0,0 +1,24 @@ +#define uint unsigned int + +uint xtc_umul_ytc(uint x, uint y) { return (x * 3) * (y * 2); } + +/* + * check-name: canonical-muldiv + * check-description: + * 1) verify that constants in mul chains are + * pushed at the right of the whole chain. + * For example '(a * 3) * b' must be canonicalized into '(a * b) * 1' + * This is needed in general for constant simplification; + * for example, for: + * '(a * 3) * (b * 2)' + * to be simplified into: + * '(a * b) * 6' + * + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * check-output-ignore + * + * check-output-excludes: \\$3 + * check-output-excludes: \\$2 + * check-output-contains: \\$6 + */ diff --git a/usr/src/tools/smatch/src/validation/cast-kinds.c b/usr/src/tools/smatch/src/validation/optim/cast-kinds.c index 697f9735ea..0ba8a156d2 100644 --- a/usr/src/tools/smatch/src/validation/cast-kinds.c +++ b/usr/src/tools/smatch/src/validation/optim/cast-kinds.c @@ -50,9 +50,13 @@ static double long_2_double(long a) { return (double)a; } static double ulong_2_double(ulong a) { return (double)a; } static double float_2_double(float a) { return (double)a; } +static float float_2_float(float a) { return a; } +static double double_2_double(double a) { return a; } + /* * check-name: cast-kinds - * check-command: test-linearize -m64 $file + * check-command: test-linearize -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast -m64 $file + * check-assert: sizeof(void *) == 8 && sizeof(long) == 8 && sizeof(double) == 8 * * check-output-start uint_2_int: @@ -64,41 +68,42 @@ uint_2_int: long_2_int: .L2: <entry-point> - scast.32 %r5 <- (64) %arg1 - ret.32 %r5 + trunc.32 %r4 <- (64) %arg1 + ret.32 %r4 ulong_2_int: .L4: <entry-point> - cast.32 %r8 <- (64) %arg1 - ret.32 %r8 + trunc.32 %r7 <- (64) %arg1 + ret.32 %r7 vptr_2_int: .L6: <entry-point> - cast.32 %r11 <- (64) %arg1 - ret.32 %r11 + trunc.32 %r10 <- (64) %arg1 + ret.32 %r10 iptr_2_int: .L8: <entry-point> - cast.32 %r14 <- (64) %arg1 + trunc.32 %r14 <- (64) %arg1 ret.32 %r14 float_2_int: .L10: <entry-point> - ret.32 %arg1 + fcvts.32 %r17 <- (32) %arg1 + ret.32 %r17 double_2_int: .L12: <entry-point> - cast.32 %r20 <- (64) %arg1 + fcvts.32 %r20 <- (64) %arg1 ret.32 %r20 @@ -111,55 +116,56 @@ int_2_uint: long_2_uint: .L16: <entry-point> - scast.32 %r26 <- (64) %arg1 - ret.32 %r26 + trunc.32 %r25 <- (64) %arg1 + ret.32 %r25 ulong_2_uint: .L18: <entry-point> - cast.32 %r29 <- (64) %arg1 - ret.32 %r29 + trunc.32 %r28 <- (64) %arg1 + ret.32 %r28 vptr_2_uint: .L20: <entry-point> - cast.32 %r32 <- (64) %arg1 - ret.32 %r32 + trunc.32 %r31 <- (64) %arg1 + ret.32 %r31 iptr_2_uint: .L22: <entry-point> - cast.32 %r35 <- (64) %arg1 + trunc.32 %r35 <- (64) %arg1 ret.32 %r35 float_2_uint: .L24: <entry-point> - ret.32 %arg1 + fcvtu.32 %r38 <- (32) %arg1 + ret.32 %r38 double_2_uint: .L26: <entry-point> - cast.32 %r41 <- (64) %arg1 + fcvtu.32 %r41 <- (64) %arg1 ret.32 %r41 int_2_long: .L28: <entry-point> - scast.64 %r44 <- (32) %arg1 + sext.64 %r44 <- (32) %arg1 ret.64 %r44 uint_2_long: .L30: <entry-point> - cast.64 %r47 <- (32) %arg1 + zext.64 %r47 <- (32) %arg1 ret.64 %r47 @@ -172,42 +178,41 @@ ulong_2_long: vptr_2_long: .L34: <entry-point> - cast.64 %r53 <- (64) %arg1 - ret.64 %r53 + ret.64 %arg1 iptr_2_long: .L36: <entry-point> - cast.64 %r56 <- (64) %arg1 - ret.64 %r56 + ret.64 %arg1 float_2_long: .L38: <entry-point> - cast.64 %r59 <- (32) %arg1 - ret.64 %r59 + fcvts.64 %r57 <- (32) %arg1 + ret.64 %r57 double_2_long: .L40: <entry-point> - ret.64 %arg1 + fcvts.64 %r60 <- (64) %arg1 + ret.64 %r60 int_2_ulong: .L42: <entry-point> - scast.64 %r65 <- (32) %arg1 - ret.64 %r65 + sext.64 %r63 <- (32) %arg1 + ret.64 %r63 uint_2_ulong: .L44: <entry-point> - cast.64 %r68 <- (32) %arg1 - ret.64 %r68 + zext.64 %r66 <- (32) %arg1 + ret.64 %r66 long_2_ulong: @@ -219,168 +224,174 @@ long_2_ulong: vptr_2_ulong: .L48: <entry-point> - cast.64 %r74 <- (64) %arg1 - ret.64 %r74 + ret.64 %arg1 iptr_2_ulong: .L50: <entry-point> - cast.64 %r77 <- (64) %arg1 - ret.64 %r77 + ret.64 %arg1 float_2_ulong: .L52: <entry-point> - cast.64 %r80 <- (32) %arg1 - ret.64 %r80 + fcvtu.64 %r76 <- (32) %arg1 + ret.64 %r76 double_2_ulong: .L54: <entry-point> - ret.64 %arg1 + fcvtu.64 %r79 <- (64) %arg1 + ret.64 %r79 int_2_vptr: .L56: <entry-point> - scast.64 %r86 <- (32) %arg1 - ret.64 %r86 + sext.64 %r82 <- (32) %arg1 + ret.64 %r82 uint_2_vptr: .L58: <entry-point> - cast.64 %r89 <- (32) %arg1 - ret.64 %r89 + zext.64 %r85 <- (32) %arg1 + ret.64 %r85 long_2_vptr: .L60: <entry-point> - scast.64 %r92 <- (64) %arg1 - ret.64 %r92 + ret.64 %arg1 ulong_2_vptr: .L62: <entry-point> - cast.64 %r95 <- (64) %arg1 - ret.64 %r95 + ret.64 %arg1 iptr_2_vptr: .L64: <entry-point> - cast.64 %r98 <- (64) %arg1 - ret.64 %r98 + ret.64 %arg1 int_2_iptr: .L66: <entry-point> - ptrcast.64 %r101 <- (32) %arg1 - ret.64 %r101 + sext.64 %r94 <- (32) %arg1 + ret.64 %r94 uint_2_iptr: .L68: <entry-point> - ptrcast.64 %r104 <- (32) %arg1 - ret.64 %r104 + zext.64 %r98 <- (32) %arg1 + ret.64 %r98 long_2_iptr: .L70: <entry-point> - ptrcast.64 %r107 <- (64) %arg1 - ret.64 %r107 + ret.64 %arg1 ulong_2_iptr: .L72: <entry-point> - ptrcast.64 %r110 <- (64) %arg1 - ret.64 %r110 + ret.64 %arg1 vptr_2_iptr: .L74: <entry-point> - ptrcast.64 %r113 <- (64) %arg1 - ret.64 %r113 + ptrcast.64 %r108 <- (64) %arg1 + ret.64 %r108 int_2_float: .L76: <entry-point> - fpcast.32 %r116 <- (32) %arg1 - ret.32 %r116 + scvtf.32 %r111 <- (32) %arg1 + ret.32 %r111 uint_2_float: .L78: <entry-point> - fpcast.32 %r119 <- (32) %arg1 - ret.32 %r119 + ucvtf.32 %r114 <- (32) %arg1 + ret.32 %r114 long_2_float: .L80: <entry-point> - fpcast.32 %r122 <- (64) %arg1 - ret.32 %r122 + scvtf.32 %r117 <- (64) %arg1 + ret.32 %r117 ulong_2_float: .L82: <entry-point> - fpcast.32 %r125 <- (64) %arg1 - ret.32 %r125 + ucvtf.32 %r120 <- (64) %arg1 + ret.32 %r120 double_2_float: .L84: <entry-point> - fpcast.32 %r128 <- (64) %arg1 - ret.32 %r128 + fcvtf.32 %r123 <- (64) %arg1 + ret.32 %r123 int_2_double: .L86: <entry-point> - fpcast.64 %r131 <- (32) %arg1 - ret.64 %r131 + scvtf.64 %r126 <- (32) %arg1 + ret.64 %r126 uint_2_double: .L88: <entry-point> - fpcast.64 %r134 <- (32) %arg1 - ret.64 %r134 + ucvtf.64 %r129 <- (32) %arg1 + ret.64 %r129 long_2_double: .L90: <entry-point> - fpcast.64 %r137 <- (64) %arg1 - ret.64 %r137 + scvtf.64 %r132 <- (64) %arg1 + ret.64 %r132 ulong_2_double: .L92: <entry-point> - fpcast.64 %r140 <- (64) %arg1 - ret.64 %r140 + ucvtf.64 %r135 <- (64) %arg1 + ret.64 %r135 float_2_double: .L94: <entry-point> - fpcast.64 %r143 <- (32) %arg1 - ret.64 %r143 + fcvtf.64 %r138 <- (32) %arg1 + ret.64 %r138 + + +float_2_float: +.L96: + <entry-point> + ret.32 %arg1 + + +double_2_double: +.L98: + <entry-point> + ret.64 %arg1 * check-output-end diff --git a/usr/src/tools/smatch/src/validation/optim/cast-nop.c b/usr/src/tools/smatch/src/validation/optim/cast-nop.c new file mode 100644 index 0000000000..7741b7a72d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/cast-nop.c @@ -0,0 +1,18 @@ +static long p2l(long *p) +{ + return (long) p; +} + +static long *l2p(long l) +{ + return (long*)l; +} + +/* + * check-name: cast-nop + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: utptr\\. + * check-output-excludes: ptrtu\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/cse-cmp-next.c b/usr/src/tools/smatch/src/validation/optim/cse-cmp-next.c new file mode 100644 index 0000000000..50fdbac097 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/cse-cmp-next.c @@ -0,0 +1,15 @@ +void foo(int p, int i, int f, int *ref, int *dst, int *src) +{ + if (p) + f = ref[i]; + if (f) + dst[i] = src[i]; +} + +/* + * check-name: cse-cmp-next + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1,2): mul\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/cse-fcmp.c b/usr/src/tools/smatch/src/validation/optim/cse-fcmp.c new file mode 100644 index 0000000000..f2a73f57fc --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/cse-fcmp.c @@ -0,0 +1,19 @@ +extern void fun(void); + +int foo(double a, double b) +{ + if (a < b) + fun(); + if (a < b) + return 1; + + return 0; +} + +/* + * check-name: cse-fcmp + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): fcmp + */ diff --git a/usr/src/tools/smatch/src/validation/optim/cse-setfval.c b/usr/src/tools/smatch/src/validation/optim/cse-setfval.c new file mode 100644 index 0000000000..15ff67d07a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/cse-setfval.c @@ -0,0 +1,12 @@ +int ftest(double a, double b) +{ + return a == 0.125 || b == 0.125; +} + +/* + * check-name: CSE OP_SETFVAL + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): setfval\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/cse-size.c b/usr/src/tools/smatch/src/validation/optim/cse-size.c new file mode 100644 index 0000000000..5b31420c84 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/cse-size.c @@ -0,0 +1,17 @@ +static void foo(void) +{ + unsigned short p = 0; + int x; + + for (;;) + if (p) + p = x; +} + +/* + * check-name: cse-size + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(2): phi\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/dup-cond0.c b/usr/src/tools/smatch/src/validation/optim/dup-cond0.c new file mode 100644 index 0000000000..26af43852c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/dup-cond0.c @@ -0,0 +1,20 @@ +struct s { + int f; +}; + +static int foo(struct s *s) +{ + if (s->f) + return 0; + else if (!s->f) + return 4; + return -1; +} + +/* + * check-name: dup-cond0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: select + */ diff --git a/usr/src/tools/smatch/src/validation/optim/ext-trunc-greater.c b/usr/src/tools/smatch/src/validation/optim/ext-trunc-greater.c new file mode 100644 index 0000000000..b682fc5deb --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/ext-trunc-greater.c @@ -0,0 +1,17 @@ +short sgt(char x) +{ + return (int) x; +} + +short ugt(unsigned char x) +{ + return (int) x; +} + +/* + * check-name: ext-trunc-greater + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: trunc\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/ext-trunc-same.c b/usr/src/tools/smatch/src/validation/optim/ext-trunc-same.c new file mode 100644 index 0000000000..2bfcf03047 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/ext-trunc-same.c @@ -0,0 +1,19 @@ +short seq(short x) +{ + return (int) x; +} + +short ueq(unsigned short x) +{ + return (int) x; +} + +/* + * check-name: ext-trunc-same + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: trunc\\. + * check-output-excludes: sext\\. + * check-output-excludes: zext\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/ext-trunc-smaller.c b/usr/src/tools/smatch/src/validation/optim/ext-trunc-smaller.c new file mode 100644 index 0000000000..194c98d7b6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/ext-trunc-smaller.c @@ -0,0 +1,18 @@ +char slt(short x) +{ + return (int) x; +} + +char ult(unsigned short x) +{ + return (int) x; +} + +/* + * check-name: ext-trunc-smaller + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: sext\\. + * check-output-excludes: zext\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/fpcast-constant.c b/usr/src/tools/smatch/src/validation/optim/fpcast-constant.c new file mode 100644 index 0000000000..49355d00db --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/fpcast-constant.c @@ -0,0 +1,13 @@ +static double foo(double a, int p) +{ + return a * ((p & 0) + 2); +} + +/* + * check-name: fpcast-constant + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-contains: scvtf\\. + * check-output-excludes: fmul\\..*\\$2 + */ diff --git a/usr/src/tools/smatch/src/validation/optim/inline-return.c b/usr/src/tools/smatch/src/validation/optim/inline-return.c new file mode 100644 index 0000000000..d075715d6a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/inline-return.c @@ -0,0 +1,24 @@ +static inline int def(void) +{ + return 1; +} + +int foo(void) +{ + return def(); +} + +int bar(void) +{ + return def(); + return 0; +} + +/* + * check-name: inline-return.c + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(2): ret\\..*\\$1 + * check-output-excludes: ret\\..*\\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/kill-casts.c b/usr/src/tools/smatch/src/validation/optim/kill-casts.c index cf52f2460e..2aa53fdaf3 100644 --- a/usr/src/tools/smatch/src/validation/kill-casts.c +++ b/usr/src/tools/smatch/src/validation/optim/kill-casts.c @@ -19,4 +19,9 @@ void foo(struct s *x) * * check-output-ignore * check-output-excludes: cast\\. + * check-output-excludes: fcvt[us]\\. + * check-output-excludes: utptr\\. + * check-output-excludes: ptrtu\\. + * check-output-excludes: [sz]ext\\. + * check-output-excludes: trunc\\. */ diff --git a/usr/src/tools/smatch/src/validation/optim/kill-stores0.c b/usr/src/tools/smatch/src/validation/optim/kill-stores0.c new file mode 100644 index 0000000000..00c005ffd4 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/kill-stores0.c @@ -0,0 +1,34 @@ +struct p { + int x, y; +}; + +struct q { + int w; +}; + +static int foo(void) +{ + int x = 1; + int y = x; + return &x == &y; +} + +static int bar(struct p p) +{ + if (p.x != 0) + ; +} + +static int baz(struct p p, struct q q) +{ + if (p.x != 0 || p.y != 1 || q.w == 0) + ; +} + +/* + * check-name: kill-stores0 + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-excludes: store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/kill-stores1.c b/usr/src/tools/smatch/src/validation/optim/kill-stores1.c new file mode 100644 index 0000000000..16a5dcf0d3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/kill-stores1.c @@ -0,0 +1,48 @@ +struct s { + int c[1]; +}; + +static struct s x, y; +static int p; + +static void foo0(void) +{ + (x = y).c; // x = y; +} + +static void foo1(void) +{ + int *t = (x = y).c; // x = y; +} + +static void foo2(void) +{ + (x = y).c + 1; // x = y; +} + +static void foo3(void) +{ + (x = y).c[0]; // x = y; +} + +static void foo4(void) +{ + (p ? x : y).c[0]; // ; +} + +static void foo5(void) +{ + (p, y).c[0]; // ; +} + +/* + * check-name: kill-stores1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(4): load\\. + * check-output-pattern(4): load\\..*0\\[y\\] + * check-output-pattern(4): store\\. + * check-output-pattern(4): store\\..*0\\[x\\] + * check-output-excludes: select\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/kill-stores2.c b/usr/src/tools/smatch/src/validation/optim/kill-stores2.c new file mode 100644 index 0000000000..861b5ece0a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/kill-stores2.c @@ -0,0 +1,17 @@ +extern void def(int *); + +static void foo(void) +{ + int c; + def(&c); + if (c) + c = c; +} + +/* + * check-name: kill-stores2 + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-excludes: store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/killed-insn.c b/usr/src/tools/smatch/src/validation/optim/killed-insn.c new file mode 100644 index 0000000000..d1cdd02eea --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/killed-insn.c @@ -0,0 +1,14 @@ +static void foo(int v) +{ + int a[2] = { }; + a; + a[1] = v; +} + +/* + * check-name: killed-insn + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-excludes: store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/live-stores0.c b/usr/src/tools/smatch/src/validation/optim/live-stores0.c new file mode 100644 index 0000000000..2cbc5ab145 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/live-stores0.c @@ -0,0 +1,29 @@ +void init(int *x); + +static int foo(void) +{ + int a[2] = { 0, 123, }; + + if (a[1] != 123) + return 1; + init(a); + if (a[1] == 123) + return 2; + return 0; +} + +#if 0 +void init(int *x) +{ + x[0] = x[1] = 0; +} +#endif + +/* + * check-name: live-stores + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-contains: store.32 *\\$123 + * check-output-pattern(2,3): store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/load-converted.c b/usr/src/tools/smatch/src/validation/optim/load-converted.c new file mode 100644 index 0000000000..91e04af4f6 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/load-converted.c @@ -0,0 +1,14 @@ +static int foo(int *p, int i) +{ + int a = p[i]; + int b = p[i]; + return (a - b); +} + +/* + * check-name: load-converted + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: add\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/load-dead.c b/usr/src/tools/smatch/src/validation/optim/load-dead.c new file mode 100644 index 0000000000..52538cc2db --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/load-dead.c @@ -0,0 +1,11 @@ +void foo(int *p) { *p; } + +int *p; +void bar(void) { *p; } + +/* + * check-name: load-dead + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * check-output-excludes: load\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/load-semi-volatile.c b/usr/src/tools/smatch/src/validation/optim/load-semi-volatile.c new file mode 100644 index 0000000000..ae3b548b8b --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/load-semi-volatile.c @@ -0,0 +1,24 @@ +struct s { + volatile int a; +}; + +struct s s; + +void foo(void) +{ + s; + s.a; +} + +/* + * check-name: load-semi-volatile + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): load + * + * check-description: + * The load at line 9 must be removed. + * The load at line 10 is volatile and thus + * must not be removed. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/lsr-and0.c b/usr/src/tools/smatch/src/validation/optim/lsr-and0.c new file mode 100644 index 0000000000..94310ba8a8 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/lsr-and0.c @@ -0,0 +1,13 @@ +unsigned lsr_and0(unsigned x) +{ + unsigned t = (x & 0x00000fff); + return (t >> 12) & t; +} + +/* + * check-name: lsr-and0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0$ + */ diff --git a/usr/src/tools/smatch/src/validation/optim/lsr-and1.c b/usr/src/tools/smatch/src/validation/optim/lsr-and1.c new file mode 100644 index 0000000000..393679e3f0 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/lsr-and1.c @@ -0,0 +1,18 @@ +// If (t >> S) is simplified into (x >> S) +// then the whole expression will be 0. +// The test is only interesting if the sub-expression +// (x & M) is referenced more than once +// (because otherwise other simplifications apply). +unsigned lsr_and1(unsigned x) +{ + unsigned t = (x & 0xfffff000); + return ((t >> 12) ^ (x >> 12)) & t; +} + +/* + * check-name: lsr-and1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0$ + */ diff --git a/usr/src/tools/smatch/src/validation/optim/lsr-asr.c b/usr/src/tools/smatch/src/validation/optim/lsr-asr.c new file mode 100644 index 0000000000..aee469404a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/lsr-asr.c @@ -0,0 +1,42 @@ +int lsrasr0(unsigned int x) +{ + return ((int) (x >> 15)) >> 15; +} + +int lsrasr1(unsigned int x) +{ + return ((int) (x >> 16)) >> 15; +} + +int lsrasr2(unsigned int x) +{ + return ((int) (x >> 16)) >> 16; +} + +/* + * check-name: lsr-asr + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +lsrasr0: +.L0: + <entry-point> + lsr.32 %r3 <- %arg1, $30 + ret.32 %r3 + + +lsrasr1: +.L2: + <entry-point> + lsr.32 %r7 <- %arg1, $31 + ret.32 %r7 + + +lsrasr2: +.L4: + <entry-point> + ret.32 $0 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/lsr-shl0.c b/usr/src/tools/smatch/src/validation/optim/lsr-shl0.c new file mode 100644 index 0000000000..952baa6b69 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/lsr-shl0.c @@ -0,0 +1,14 @@ +unsigned mask(unsigned x) +{ + return (x << 15) >> 15; +} + +/* + * check-name: lsr-shl0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*0x1ffff + * check-output-excludes: lsr\\. + * check-output-excludes: shl\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/mask-lsr.c b/usr/src/tools/smatch/src/validation/optim/mask-lsr.c new file mode 100644 index 0000000000..ec636444a1 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/mask-lsr.c @@ -0,0 +1,14 @@ +// ((x & M) | y) >> S to (y >> S) when (M >> S) == 0 + +unsigned int foo(unsigned int x, unsigned int y) +{ + return ((x & 0xff) | y) >> 8; +} + +/* + * check-name: mask-lsr + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: %arg1 + */ diff --git a/usr/src/tools/smatch/src/validation/optim/mask-out.c b/usr/src/tools/smatch/src/validation/optim/mask-out.c new file mode 100644 index 0000000000..ac85aec800 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/mask-out.c @@ -0,0 +1,12 @@ +unsigned mask(unsigned a, unsigned b) +{ + return ((a & 0xffff0000) | b) & 0x0000ffff; +} + +/* + * check-name: mask-out + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: %arg1 + */ diff --git a/usr/src/tools/smatch/src/validation/optim/mask1-setne0.c b/usr/src/tools/smatch/src/validation/optim/mask1-setne0.c new file mode 100644 index 0000000000..1e599dc803 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/mask1-setne0.c @@ -0,0 +1,28 @@ +struct s { + unsigned i:1; +}; + +int foo(struct s x) +{ + unsigned int i = x.i; + + if (i == 0) + return 1; + else if (i == 1) + return 1; + return 0; +} + +/* + * check-name: mask1-setne0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + ret.32 $1 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/missing-select.c b/usr/src/tools/smatch/src/validation/optim/missing-select.c new file mode 100644 index 0000000000..07ea008f9f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/missing-select.c @@ -0,0 +1,23 @@ +static int foo(int **g) +{ + int i = 1; + int *a[2]; + int **p; + + a[1] = &i; + if (g) + p = g; + else + p = &a[0]; + if (!g) + **p = 0; + return i; +} + +/* + * check-name: missing-select + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: select\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/muldiv-minus-one.c b/usr/src/tools/smatch/src/validation/optim/muldiv-minus-one.c index 42b086afd8..7e2d7bb7fb 100644 --- a/usr/src/tools/smatch/src/validation/optim/muldiv-minus-one.c +++ b/usr/src/tools/smatch/src/validation/optim/muldiv-minus-one.c @@ -14,5 +14,5 @@ u32 udivm1(u32 a) { return a / (u32) -1; } * check-output-excludes: divs\\. * check-output-contains: neg\\. * check-output-contains: divu\\. - * check-output-pattern-3-times: neg\\. + * check-output-pattern(3): neg\\. */ diff --git a/usr/src/tools/smatch/src/validation/optim/null-phi.c b/usr/src/tools/smatch/src/validation/optim/null-phi.c new file mode 100644 index 0000000000..1f9de4d5ba --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/null-phi.c @@ -0,0 +1,9 @@ +static int foo(void) +{ + if (0) + return 0; +} + +/* + * check-name: null-phi + */ diff --git a/usr/src/tools/smatch/src/validation/optim/or-and-constant1.c b/usr/src/tools/smatch/src/validation/optim/or-and-constant1.c new file mode 100644 index 0000000000..aa673b9053 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/or-and-constant1.c @@ -0,0 +1,29 @@ +unsigned int and_or_equ(unsigned int a) +{ + return (a | 3) & 3; +} + +int and_or_eqs(int a) +{ + return (a | 3) & 3; +} + +unsigned int or_and_equ(unsigned int a) +{ + return (a & 3) | 3; +} + +int or_and_eqs(int a) +{ + return (a & 3) | 3; +} + +/* + * check-name: or-and-constant1 + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-pattern(4): ret\\..*\\$3 + * check-output-excludes: or\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/phi-ret.c b/usr/src/tools/smatch/src/validation/optim/phi-ret.c new file mode 100644 index 0000000000..bc3e04749f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/phi-ret.c @@ -0,0 +1,21 @@ +int foo(int p, int q, int v) +{ + if (q) { + if (p) { + v = p; + p = 0; + } + } else + p = 0; + if (p) + return v + 1; + return q; +} + +/* + * check-name: phi-ret + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: phi\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/restrict.c b/usr/src/tools/smatch/src/validation/optim/restrict.c new file mode 100644 index 0000000000..de6289e2b8 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/restrict.c @@ -0,0 +1,73 @@ +extern int g, h; + +void f00u(int *s) +{ + g = *s; + h = *s; +} + +void f00r(int *restrict s) +{ + g = *s; + h = *s; +} + + +void f01u(int *a, int *b, int *s) +{ + *a = *s; + *b = *s; +} + +void f01r(int *restrict a, int *restrict b, int *restrict s) +{ + *a = *s; + *b = *s; +} + +/* + * check-name: optim/restrict + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-start +f00u: +.L0: + <entry-point> + load.32 %r2 <- 0[%arg1] + store.32 %r2 -> 0[g] + load.32 %r4 <- 0[%arg1] + store.32 %r4 -> 0[h] + ret + + +f00r: +.L2: + <entry-point> + load.32 %r6 <- 0[%arg1] + store.32 %r6 -> 0[g] + store.32 %r6 -> 0[h] + ret + + +f01u: +.L4: + <entry-point> + load.32 %r10 <- 0[%arg3] + store.32 %r10 -> 0[%arg1] + load.32 %r13 <- 0[%arg3] + store.32 %r13 -> 0[%arg2] + ret + + +f01r: +.L6: + <entry-point> + load.32 %r16 <- 0[%arg3] + store.32 %r16 -> 0[%arg1] + store.32 %r16 -> 0[%arg2] + ret + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/select-zero.c b/usr/src/tools/smatch/src/validation/optim/select-zero.c new file mode 100644 index 0000000000..ed737bffc2 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/select-zero.c @@ -0,0 +1,16 @@ +static int sel0(int a) +{ + if (a) + return 0; + else + return a; +} + +/* + * check-name: select-zero + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-contains: ret.32 *\\$0 + * check-output-excludes: select\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/setcc-mask.c b/usr/src/tools/smatch/src/validation/optim/setcc-mask.c new file mode 100644 index 0000000000..5d27178805 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/setcc-mask.c @@ -0,0 +1,18 @@ +int foo (int a) +{ + return ((a == 0) & 1) == (a == 0); +} + +/* + * check-name: setcc-mask + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + ret.32 $1 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/setne0-sext.c b/usr/src/tools/smatch/src/validation/optim/setne0-sext.c new file mode 100644 index 0000000000..4167979b8b --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/setne0-sext.c @@ -0,0 +1,9 @@ +long foo(int a) { return a != 0; } + +/* + * check-name: setne0-sext + * check-command: test-linearize -m64 -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: sext\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/setne0-trunc.c b/usr/src/tools/smatch/src/validation/optim/setne0-trunc.c new file mode 100644 index 0000000000..6c5494ecaa --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/setne0-trunc.c @@ -0,0 +1,9 @@ +char foo(int a) { return a != 0; } + +/* + * check-name: setne0-trunc + * check-command: test-linearize -m64 -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: trunc\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/setne0-zext.c b/usr/src/tools/smatch/src/validation/optim/setne0-zext.c new file mode 100644 index 0000000000..8a074f0372 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/setne0-zext.c @@ -0,0 +1,9 @@ +unsigned long foo(int a) { return (unsigned int) (a != 0); } + +/* + * check-name: setne0-zext + * check-command: test-linearize -m64 -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: zext\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/sext-sext.c b/usr/src/tools/smatch/src/validation/optim/sext-sext.c new file mode 100644 index 0000000000..604a7dd4f0 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/sext-sext.c @@ -0,0 +1,12 @@ +int foo(signed char offset) +{ + return (int)(short) offset; +} + +/* + * check-name: sext-sext + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): sext\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/sext.c b/usr/src/tools/smatch/src/validation/optim/sext.c new file mode 100644 index 0000000000..719730d507 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/sext.c @@ -0,0 +1,15 @@ +int sext(int x) +{ + return (x << 5) >> 5; +} + +/* + * check-name: sext + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: sext\\.$27 + * check-output-excludes: asr\\. + * check-output-excludes: shl\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/sh-or-and0.c b/usr/src/tools/smatch/src/validation/optim/sh-or-and0.c new file mode 100644 index 0000000000..02f0cb0351 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/sh-or-and0.c @@ -0,0 +1,20 @@ +unsigned lsr_or_and0(unsigned x, unsigned b) +{ + return (((x & 0x00000fff) | b) >> 12); +} + +unsigned shl_or_and0(unsigned x, unsigned b) +{ + return (((x & 0xfff00000) | b) << 12); +} + +/* + * check-name: sh-or-and0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): lsr\\. + * check-output-pattern(1): shl\\. + * check-output-excludes: or\\. + * check-output-excludes: and\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/sh-or-and1.c b/usr/src/tools/smatch/src/validation/optim/sh-or-and1.c new file mode 100644 index 0000000000..7b79bbf32a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/sh-or-and1.c @@ -0,0 +1,20 @@ +unsigned lsr_or_and1(unsigned x, unsigned b) +{ + return (((x & 0xfffff000) | b) >> 12); +} + +unsigned shl_or_and1(unsigned x, unsigned b) +{ + return (((x & 0x000fffff) | b) << 12); +} + +/* + * check-name: sh-or-and1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): lsr\\. + * check-output-pattern(1): shl\\. + * check-output-pattern(2): or\\. + * check-output-excludes: and\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/sh-or-and2.c b/usr/src/tools/smatch/src/validation/optim/sh-or-and2.c new file mode 100644 index 0000000000..241aeaff2d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/sh-or-and2.c @@ -0,0 +1,21 @@ +unsigned lsr_or_and2(unsigned x, unsigned b) +{ + return (((x & 0xf0ffffff) | b) >> 12); +} + +unsigned shl_or_and2(unsigned x, unsigned b) +{ + return (((x & 0xffffff0f) | b) << 12); +} + +/* + * check-name: sh-or-and2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): lsr\\. + * check-output-pattern(1): shl\\. + * check-output-pattern(2): or\\. + * check-output-pattern(1): and\\..*\\$0xf0fff000 + * check-output-pattern(1): and\\..*\\$0xfff0f + */ diff --git a/usr/src/tools/smatch/src/validation/optim/shift-big.c b/usr/src/tools/smatch/src/validation/optim/shift-big.c new file mode 100644 index 0000000000..84bcd2ce01 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/shift-big.c @@ -0,0 +1,82 @@ +typedef unsigned int u32; +typedef int s32; + +s32 asr31(s32 a) { return a >> 31; } +s32 asr32(s32 a) { return a >> 32; } +s32 asr33(s32 a) { return a >> 33; } + +u32 lsr31(u32 a) { return a >> 31; } +u32 lsr32(u32 a) { return a >> 32; } +u32 lsr33(u32 a) { return a >> 33; } + +u32 shl31(u32 a) { return a << 31; } +u32 shl32(u32 a) { return a << 32; } +u32 shl33(u32 a) { return a << 33; } + +/* + * check-name: optim/shift-big.c + * check-command: test-linearize -Wno-decl -m64 $file + * + * check-error-ignore + * check-output-start +asr31: +.L0: + <entry-point> + asr.32 %r2 <- %arg1, $31 + ret.32 %r2 + + +asr32: +.L2: + <entry-point> + asr.32 %r5 <- %arg1, $32 + ret.32 %r5 + + +asr33: +.L4: + <entry-point> + asr.32 %r8 <- %arg1, $33 + ret.32 %r8 + + +lsr31: +.L6: + <entry-point> + lsr.32 %r11 <- %arg1, $31 + ret.32 %r11 + + +lsr32: +.L8: + <entry-point> + ret.32 $0 + + +lsr33: +.L10: + <entry-point> + ret.32 $0 + + +shl31: +.L12: + <entry-point> + shl.32 %r20 <- %arg1, $31 + ret.32 %r20 + + +shl32: +.L14: + <entry-point> + ret.32 $0 + + +shl33: +.L16: + <entry-point> + ret.32 $0 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/shift-shift.c b/usr/src/tools/smatch/src/validation/optim/shift-shift.c new file mode 100644 index 0000000000..12a4b7d4cb --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/shift-shift.c @@ -0,0 +1,149 @@ +unsigned int shl0(unsigned int x) +{ + return x << 15 << 15; +} + +unsigned int shl1(unsigned int x) +{ + return x << 16 << 15; +} + +unsigned int shl2(unsigned int x) +{ + return x << 16 << 16; +} + +unsigned int shl3(unsigned int x) +{ + return x << 12 << 10 << 10; +} + + +unsigned int lsr0(unsigned int x) +{ + return x >> 15 >> 15; +} + +unsigned int lsr1(unsigned int x) +{ + return x >> 16 >> 15; +} + +unsigned int lsr2(unsigned int x) +{ + return x >> 16 >> 16; +} + +unsigned int lsr3(unsigned int x) +{ + return x >> 12 >> 10 >> 10; +} + + +int asr0(int x) +{ + return x >> 15 >> 15; +} + +int asr1(int x) +{ + return x >> 16 >> 15; +} + +int asr2(int x) +{ + return x >> 16 >> 16; +} + +int asr3(int x) +{ + return x >> 12 >> 10 >> 10; +} + +/* + * check-name: shift-shift + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +shl0: +.L0: + <entry-point> + shl.32 %r3 <- %arg1, $30 + ret.32 %r3 + + +shl1: +.L2: + <entry-point> + shl.32 %r7 <- %arg1, $31 + ret.32 %r7 + + +shl2: +.L4: + <entry-point> + ret.32 $0 + + +shl3: +.L6: + <entry-point> + ret.32 $0 + + +lsr0: +.L8: + <entry-point> + lsr.32 %r20 <- %arg1, $30 + ret.32 %r20 + + +lsr1: +.L10: + <entry-point> + lsr.32 %r24 <- %arg1, $31 + ret.32 %r24 + + +lsr2: +.L12: + <entry-point> + ret.32 $0 + + +lsr3: +.L14: + <entry-point> + ret.32 $0 + + +asr0: +.L16: + <entry-point> + asr.32 %r37 <- %arg1, $30 + ret.32 %r37 + + +asr1: +.L18: + <entry-point> + asr.32 %r41 <- %arg1, $31 + ret.32 %r41 + + +asr2: +.L20: + <entry-point> + asr.32 %r45 <- %arg1, $31 + ret.32 %r45 + + +asr3: +.L22: + <entry-point> + asr.32 %r50 <- %arg1, $31 + ret.32 %r50 + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/optim/shift-zext.c b/usr/src/tools/smatch/src/validation/optim/shift-zext.c new file mode 100644 index 0000000000..30409beca3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/shift-zext.c @@ -0,0 +1,12 @@ +unsigned int foo(unsigned int x) +{ + return (x << 20) >> 20; +} + +/* + * check-name: shift-zext + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*%arg1, \\$0xfff + */ diff --git a/usr/src/tools/smatch/src/validation/optim/shl-and0.c b/usr/src/tools/smatch/src/validation/optim/shl-and0.c new file mode 100644 index 0000000000..894bd88234 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/shl-and0.c @@ -0,0 +1,13 @@ +unsigned shl_and0(unsigned x) +{ + unsigned t = (x & 0xfff00000); + return (t << 12) & t; +} + +/* + * check-name: shl-and0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0$ + */ diff --git a/usr/src/tools/smatch/src/validation/optim/shl-and1.c b/usr/src/tools/smatch/src/validation/optim/shl-and1.c new file mode 100644 index 0000000000..13a1675bed --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/shl-and1.c @@ -0,0 +1,18 @@ +// If (t << S) is simplified into (x << S) +// then the whole expression will be 0. +// The test is only interesting if the sub-expression +// (x & M) is referenced more than once +// (because otherwise other simplifications apply). +unsigned shl_and1(unsigned x) +{ + unsigned t = (x & 0x000fffff); + return ((t << 12) ^ (x << 12)) & t; +} + +/* + * check-name: shl-and1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0$ + */ diff --git a/usr/src/tools/smatch/src/validation/optim/shl-lsr0.c b/usr/src/tools/smatch/src/validation/optim/shl-lsr0.c new file mode 100644 index 0000000000..fc2561bd1f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/shl-lsr0.c @@ -0,0 +1,14 @@ +unsigned mask(unsigned x) +{ + return (x >> 15) << 15; +} + +/* + * check-name: shl-lsr0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*0xffff8000 + * check-output-excludes: lsr\\. + * check-output-excludes: shl\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/store-dominated.c b/usr/src/tools/smatch/src/validation/optim/store-dominated.c new file mode 100644 index 0000000000..5780ba6b0d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/store-dominated.c @@ -0,0 +1,15 @@ +static int a[]; + +static void foo(void) +{ + int *c = &a[1]; + *c = *c = 0; +} + +/* + * check-name: store-dominated + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-excludes: add\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/trivial-phis.c b/usr/src/tools/smatch/src/validation/optim/trivial-phis.c new file mode 100644 index 0000000000..8af093c1a1 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/trivial-phis.c @@ -0,0 +1,14 @@ +void foo(int a) +{ + while (1) + a ^= 0; +} + +/* + * check-name: trivial phis + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: phi\\. + * check-output-excludes: phisrc\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/trunc-mask-zext.c b/usr/src/tools/smatch/src/validation/optim/trunc-mask-zext.c new file mode 100644 index 0000000000..9b60417468 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/trunc-mask-zext.c @@ -0,0 +1,13 @@ +unsigned long long foo(unsigned long long x) +{ + return (((unsigned int) x) & 0x7ffU); +} + +/* + * check-name: trunc-mask-zext + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: trunc\\. + * check-output-excludes: zext\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/trunc-or-shl.c b/usr/src/tools/smatch/src/validation/optim/trunc-or-shl.c new file mode 100644 index 0000000000..70d8bd1de5 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/trunc-or-shl.c @@ -0,0 +1,13 @@ +char foo(int a, int b) +{ + return (a << 8) | b; +} + +/* + * check-name: trunc-or-shl + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: ret\\..*%arg2 + */ diff --git a/usr/src/tools/smatch/src/validation/optim/trunc-seteq0.c b/usr/src/tools/smatch/src/validation/optim/trunc-seteq0.c new file mode 100644 index 0000000000..5994b17cd9 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/trunc-seteq0.c @@ -0,0 +1,18 @@ +struct S { + int :1; + signed int s:2; + unsigned int u:3; +}; + +int os(int i, struct S *b) { return i || b->s; } +int ou(int i, struct S *b) { return i || b->u; } + +/* + * check-name: trunc-seteq0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: trunc\\. + * check-output-excludes: sext\\. + * check-output-excludes: zext\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/trunc-setne0.c b/usr/src/tools/smatch/src/validation/optim/trunc-setne0.c new file mode 100644 index 0000000000..878c05fa62 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/trunc-setne0.c @@ -0,0 +1,20 @@ +struct s { + unsigned int u:1; +}; + +unsigned int foo(struct s x) +{ + if (x.u) + return 1; + else + return 0; +} + +/* + * check-name: trunc-setne0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\. + * check-output-excludes: trunc\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/trunc-trunc.c b/usr/src/tools/smatch/src/validation/optim/trunc-trunc.c new file mode 100644 index 0000000000..6dc50aeef3 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/trunc-trunc.c @@ -0,0 +1,12 @@ +char foo(int a) +{ + return ((((short) a) + 1) - 1); +} + +/* + * check-name: trunc-trunc + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): trunc\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/volatile-bitfield.c b/usr/src/tools/smatch/src/validation/optim/volatile-bitfield.c new file mode 100644 index 0000000000..99db44014f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/volatile-bitfield.c @@ -0,0 +1,16 @@ +struct s { + int f:3; +}; + +void foo(volatile struct s *p) +{ + p->f; +} + +/* + * check-name: volatile-bitfield + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: load\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/volatile-side-effect.c b/usr/src/tools/smatch/src/validation/optim/volatile-side-effect.c new file mode 100644 index 0000000000..842b722827 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/volatile-side-effect.c @@ -0,0 +1,13 @@ +void foo(int p, volatile int *ptr) +{ + p ? : *ptr; + p ? : *ptr; +} + +/* + * check-name: volatile-side-effect + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * + * check-output-pattern(2): load + */ diff --git a/usr/src/tools/smatch/src/validation/optim/volatile-store00.c b/usr/src/tools/smatch/src/validation/optim/volatile-store00.c new file mode 100644 index 0000000000..451eefa157 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/volatile-store00.c @@ -0,0 +1,27 @@ +void foo(volatile int *p) +{ + *p = 0; + *p = 0; +} + +void bar(void) +{ + extern volatile int i; + i = 0; + i = 0; +} + + +void baz(void) +{ + volatile int i; + i = 0; + i = 0; +} + +/* + * check-name: keep volatile stores + * check-command: test-linearize -Wno-decl -fdump-ir=final $file + * check-output-ignore + * check-output-pattern(6): store\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/zext-and.c b/usr/src/tools/smatch/src/validation/optim/zext-and.c new file mode 100644 index 0000000000..a3153bf78a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/zext-and.c @@ -0,0 +1,12 @@ +unsigned int foo(unsigned char x) +{ + return (unsigned int)x & 0xffffU; +} + +/* + * check-name: zext-and + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: and\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/zext-and1.c b/usr/src/tools/smatch/src/validation/optim/zext-and1.c new file mode 100644 index 0000000000..c99a0e6244 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/zext-and1.c @@ -0,0 +1,12 @@ +unsigned int bar(unsigned char x) +{ + return (unsigned int)x & 0xff01U; +} + +/* + * check-name: zext-and1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*\\$1 + */ diff --git a/usr/src/tools/smatch/src/validation/optim/zext-asr.c b/usr/src/tools/smatch/src/validation/optim/zext-asr.c new file mode 100644 index 0000000000..5f235ad806 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/zext-asr.c @@ -0,0 +1,13 @@ +unsigned short foo(unsigned short a) +{ + return a >> 16; +} + +/* + * check-name: zext-asr + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0 + * check-output-excludes: asr\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/zext-sext.c b/usr/src/tools/smatch/src/validation/optim/zext-sext.c new file mode 100644 index 0000000000..1fe3900d15 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/zext-sext.c @@ -0,0 +1,13 @@ +int foo(unsigned char offset) +{ + return (int)(short) offset; +} + +/* + * check-name: zext-sext + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: sext\\. + * check-output-pattern(1): zext\\. + */ diff --git a/usr/src/tools/smatch/src/validation/optim/zext-zext.c b/usr/src/tools/smatch/src/validation/optim/zext-zext.c new file mode 100644 index 0000000000..986d22422a --- /dev/null +++ b/usr/src/tools/smatch/src/validation/optim/zext-zext.c @@ -0,0 +1,13 @@ +int foo(unsigned char offset) +{ + return (int)(unsigned short) offset; +} + +/* + * check-name: zext-zext + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: sext\\. + * check-output-pattern(1): zext\\. + */ diff --git a/usr/src/tools/smatch/src/validation/option-parsing-00.c b/usr/src/tools/smatch/src/validation/option-parsing-00.c new file mode 100644 index 0000000000..9dceab7fab --- /dev/null +++ b/usr/src/tools/smatch/src/validation/option-parsing-00.c @@ -0,0 +1,5 @@ + +/* + * check-name: option parsing 00 + * check-command: sparse -foptimize-xyz $file + */ diff --git a/usr/src/tools/smatch/src/validation/option-parsing-01.c b/usr/src/tools/smatch/src/validation/option-parsing-01.c new file mode 100644 index 0000000000..a2875bd15e --- /dev/null +++ b/usr/src/tools/smatch/src/validation/option-parsing-01.c @@ -0,0 +1,5 @@ + +/* + * check-name: option parsing 01 + * check-command: sparse -fno-optimize-xyz $file + */ diff --git a/usr/src/tools/smatch/src/validation/overflow.c b/usr/src/tools/smatch/src/validation/overflow.c new file mode 100644 index 0000000000..c2655e329b --- /dev/null +++ b/usr/src/tools/smatch/src/validation/overflow.c @@ -0,0 +1,19 @@ +extern int a; + +int a = __INT_MAX__ * 2; + +int foo(void) +{ + return __INT_MAX__ * 2; +} + +/* + * check-name: overflow + * check-command: sparse -Wno-decl $file + * + * check-known-to-fail + * check-error-start +bug-overflow.c:3:21: warning: integer overflow in expression +bug-overflow.c:7:28: warning: integer overflow in expression + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/base-file.c b/usr/src/tools/smatch/src/validation/preprocessor/base-file.c new file mode 100644 index 0000000000..61a290cb06 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/base-file.c @@ -0,0 +1,17 @@ +__FILE__ +__BASE_FILE__ + +#include "base-file.h" + +/* + * check-name: base file + * check-command: sparse -E $file + * + * check-output-start + +"preprocessor/base-file.c" +"preprocessor/base-file.c" +"preprocessor/base-file.h" +"preprocessor/base-file.c" + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/base-file.h b/usr/src/tools/smatch/src/validation/preprocessor/base-file.h new file mode 100644 index 0000000000..018b16c541 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/base-file.h @@ -0,0 +1,2 @@ +__FILE__ +__BASE_FILE__ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/builtin.c b/usr/src/tools/smatch/src/validation/preprocessor/builtin.c new file mode 100644 index 0000000000..6c3aa1760b --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/builtin.c @@ -0,0 +1,17 @@ +__CHECKER__ +F(__CHECKER__,__CHECKER__) +S(#__CHECKER__) +const char str[] = "__CHECKER__"; + +/* + * check-name: builtin + * check-command: sparse -E $file + * + * check-output-start + +1 +F(1,1) +S(#1) +const char str[] = "__CHECKER__"; + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/cli-D-arg.c b/usr/src/tools/smatch/src/validation/preprocessor/cli-D-arg.c new file mode 100644 index 0000000000..03c5bac348 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/cli-D-arg.c @@ -0,0 +1,12 @@ +A +B +/* + * check-name: cli: -D MACRO + * check-command: sparse -E -D A -D B=abc $file + * + * check-output-start + +1 +abc + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/cli-D-space.c b/usr/src/tools/smatch/src/validation/preprocessor/cli-D-space.c new file mode 100644 index 0000000000..8343bf1acf --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/cli-D-space.c @@ -0,0 +1,10 @@ +M(0,1) +/* + * check-name: cli: allow spaces in macros + * check-command: sparse -E '-DM(X, Y)=a' $file + * + * check-output-start + +a + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/dump-macros-empty.c b/usr/src/tools/smatch/src/validation/preprocessor/dump-macros-empty.c index 672c66c711..c9cfe01075 100644 --- a/usr/src/tools/smatch/src/validation/preprocessor/dump-macros-empty.c +++ b/usr/src/tools/smatch/src/validation/preprocessor/dump-macros-empty.c @@ -3,5 +3,5 @@ * check-command: sparse -E -dD empty-file * * check-output-ignore -check-output-pattern-1-times: #define __CHECKER__ 1 +check-output-pattern(1): #define __CHECKER__ 1 */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/dump-macros-multi.c b/usr/src/tools/smatch/src/validation/preprocessor/dump-macros-multi.c index 2f6e8d04fb..c6c334cfcc 100644 --- a/usr/src/tools/smatch/src/validation/preprocessor/dump-macros-multi.c +++ b/usr/src/tools/smatch/src/validation/preprocessor/dump-macros-multi.c @@ -3,5 +3,5 @@ * check-command: sparse -E -dD empty-file $file * * check-output-ignore -check-output-pattern-2-times: #define __CHECKER__ 1 +check-output-pattern(2): #define __CHECKER__ 1 */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/dump-macros-only.c b/usr/src/tools/smatch/src/validation/preprocessor/dump-macros-only.c new file mode 100644 index 0000000000..cee6f8700d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/dump-macros-only.c @@ -0,0 +1,36 @@ + +#define ABC abc +#undef ABC + +#define DEF def +#undef DEF +#define DEF xyz + +#define NYDEF ydef + +#define STRING(x) #x +#define CONCAT(x,y) x ## y + +#define unlocks(...) annotate(unlock_func(__VA_ARGS__)) +#define apply(x,...) x(__VA_ARGS__) + +int main(int argc, char *argv[]) +{ + return 0; +} +/* + * check-name: dump-macros only -dM + * check-command: sparse -E -dM -DIJK=ijk -UNDEF -UNYDEF $file + * + * check-output-ignore +check-output-pattern(1): #define __CHECKER__ 1 +check-output-contains: #define IJK ijk +check-output-contains: #define DEF xyz +check-output-contains: #define NYDEF ydef +check-output-contains: #define STRING(x) #x +check-output-contains: #define CONCAT(x,y) x ## y +check-output-contains: #define unlocks(...) annotate(unlock_func(__VA_ARGS__)) +check-output-contains: #define apply(x,...) x(__VA_ARGS__) +check-output-excludes: int main(int argc, char \\*argv\\[\\]) +check-output-excludes: ^\\[^#] + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/dump-macros.c b/usr/src/tools/smatch/src/validation/preprocessor/dump-macros.c index 79f3de6a2c..dc2d7d19a8 100644 --- a/usr/src/tools/smatch/src/validation/preprocessor/dump-macros.c +++ b/usr/src/tools/smatch/src/validation/preprocessor/dump-macros.c @@ -6,13 +6,29 @@ #define DEF xyz #define NYDEF ydef + +#define STRING(x) #x +#define CONCAT(x,y) x ## y + +#define unlocks(...) annotate(unlock_func(__VA_ARGS__)) +#define apply(x,...) x(__VA_ARGS__) + +int main(int argc, char *argv[]) +{ + return 0; +} /* * check-name: dump-macros * check-command: sparse -E -dD -DIJK=ijk -UNDEF -UNYDEF $file * * check-output-ignore -check-output-pattern-1-times: #define __CHECKER__ 1 +check-output-pattern(1): #define __CHECKER__ 1 check-output-contains: #define IJK ijk check-output-contains: #define DEF xyz check-output-contains: #define NYDEF ydef +check-output-contains: #define STRING(x) #x +check-output-contains: #define CONCAT(x,y) x ## y +check-output-contains: #define unlocks(...) annotate(unlock_func(__VA_ARGS__)) +check-output-contains: #define apply(x,...) x(__VA_ARGS__) +check-output-contains: int main(int argc, char \\*argv\\[\\]) */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/dynamic.c b/usr/src/tools/smatch/src/validation/preprocessor/dynamic.c new file mode 100644 index 0000000000..f791ba3956 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/dynamic.c @@ -0,0 +1,37 @@ +#if defined(__LINE__) +__LINE__ +#endif +#if defined(__FILE__) +__FILE__ +#endif +#if defined(__BASE_FILE__) +__BASE_FILE__ +#endif +#if defined(__DATE__) +date +#endif +#if defined(__TIME__) +time +#endif +#if defined(__COUNTER__) +counter +#endif +#if defined(__INCLUDE_LEVEL__) +__INCLUDE_LEVEL__ +#endif + +/* + * check-name: dynamic-macros + * check-command: sparse -E $file + * + * check-output-start + +2 +"preprocessor/dynamic.c" +"preprocessor/dynamic.c" +date +time +counter +0 + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/extra-token.c b/usr/src/tools/smatch/src/validation/preprocessor/extra-token.c new file mode 100644 index 0000000000..50a853c5d0 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/extra-token.c @@ -0,0 +1,15 @@ +#define THIS 0 +#ifdef THIS == 1 +#endif + +/* + * check-name: preprocessor/extra-token.c + * check-command: sparse -E $file + * check-known-to-fail + * + * check-error-start +preprocessor/extra-token.c:2:13: warning: extra tokens at end of #ifdef directive + * check-error-end + * + * check-output-ignore + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/has-attribute.c b/usr/src/tools/smatch/src/validation/preprocessor/has-attribute.c new file mode 100644 index 0000000000..3149cbfa63 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/has-attribute.c @@ -0,0 +1,56 @@ +#ifndef __has_attribute +__has_attribute()??? Quesako? +#define __has_attribute(x) 0 +#else +"has __has_attribute(), yeah!" +#endif + +123 __has_attribute(nothinx) def + +#if __has_attribute(nothinx) +#error "not a attribute!" +#endif + +#if 1 \ + && __has_attribute(packed) \ + && __has_attribute(aligned) \ + && __has_attribute(const) \ + && __has_attribute(pure) \ + && __has_attribute(noreturn) \ + && __has_attribute(designated_init) \ + && __has_attribute(transparent_union) \ + +"ok gcc" +#endif + +#if 1 \ + && __has_attribute(fastcall) \ + +"ok gcc ignore" +#endif + +#if 1 \ + && __has_attribute(nocast) \ + && __has_attribute(noderef) \ + && __has_attribute(safe) \ + && __has_attribute(force) \ + && __has_attribute(bitwise) \ + && __has_attribute(address_space) \ + && __has_attribute(context) \ + +"ok sparse specific" +#endif + +/* + * check-name: has-attribute + * check-command: sparse -E $file + * + * check-output-start + +"has __has_attribute(), yeah!" +123 0 def +"ok gcc" +"ok gcc ignore" +"ok sparse specific" + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/has-builtin.c b/usr/src/tools/smatch/src/validation/preprocessor/has-builtin.c new file mode 100644 index 0000000000..03272fc95c --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/has-builtin.c @@ -0,0 +1,43 @@ +#ifndef __has_builtin +__has_builtin()??? Quesako? +#define __has_builtin(x) 0 +#else +"has __has_builtin(), yeah!" +#endif + +#if __has_builtin(nothing) +#error "not a builtin!" +#endif + +#if __has_builtin(__builtin_offsetof) \ + || __has_builtin(__builtin_types_compatible_p) +#error "builtin ops are not builtin functions!" +#endif + +#if __has_builtin(__builtin_va_list) \ + || __has_builtin(__builtin_ms_va_list) +#error "builtin types are not builtin functions!" +#endif + +#if __has_builtin(__builtin_abs) +abs +#endif + +#if __has_builtin(__builtin_constant_p) +constant_p +#endif + +123 __has_builtin(abc) def + +/* + * check-name: has-builtin + * check-command: sparse -E $file + * + * check-output-start + +"has __has_builtin(), yeah!" +abs +constant_p +123 0 def + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/ident-pragma.c b/usr/src/tools/smatch/src/validation/preprocessor/ident-pragma.c new file mode 100644 index 0000000000..81abdd7b10 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/ident-pragma.c @@ -0,0 +1,12 @@ +#pragma ident "foo" + +/* + * check-description: check that '#pragma ident ... " is ignored. + * check-name: ident-pragma + * check-command: sparse -E $file + * + * check-output-start + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/ident.c b/usr/src/tools/smatch/src/validation/preprocessor/ident.c new file mode 100644 index 0000000000..3e7f3ad09b --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/ident.c @@ -0,0 +1,12 @@ +#ident "foo" + +/* + * check-description: check that the non-standard "#ident ..." is ignored. + * check-name: ident + * check-command: sparse -E $file + * + * check-output-start + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/include-level.c b/usr/src/tools/smatch/src/validation/preprocessor/include-level.c new file mode 100644 index 0000000000..b5e5e60363 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/include-level.c @@ -0,0 +1,14 @@ +__FILE__: __INCLUDE_LEVEL__ + +#include "include-level.h" + +/* + * check-name: include-level + * check-command: sparse -E $file + * + * check-output-start + +"preprocessor/include-level.c": 0 +"preprocessor/include-level.h": 1 + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/include-level.h b/usr/src/tools/smatch/src/validation/preprocessor/include-level.h new file mode 100644 index 0000000000..cbc101825e --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/include-level.h @@ -0,0 +1 @@ +__FILE__: __INCLUDE_LEVEL__ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/missing-delim.c b/usr/src/tools/smatch/src/validation/preprocessor/missing-delim.c new file mode 100644 index 0000000000..60957fb9b4 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/missing-delim.c @@ -0,0 +1,17 @@ +static int c = 'a; + +static char s[] = "abc; +static char t[] = "xyz"; + +extern void foo(void); + +/* + * check-name: missing-delim + * check-command: sparse -E $file + * check-output-ignore + * + * check-error-start +preprocessor/missing-delim.c:2:0: warning: missing terminating ' character +preprocessor/missing-delim.c:4:0: warning: missing terminating " character + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/phase2/backslash b/usr/src/tools/smatch/src/validation/preprocessor/phase2-backslash.c index 29c85b4d0f..21d94d7dc8 100644 --- a/usr/src/tools/smatch/src/validation/phase2/backslash +++ b/usr/src/tools/smatch/src/validation/preprocessor/phase2-backslash.c @@ -17,11 +17,26 @@ * the rest of tokenizer. */ +/* + * check-name: phase2-backslash + * check-command: sparse -E $file + * + * check-output-start + +"\a" +1 +D +'\a' + * check-output-end + * + * check-error-start +preprocessor/phase2-backslash.c:68:0: warning: backslash-newline at end of file + * check-error-end + */ + #define A(x) #x #define B(x) A(x) /* This should result in "\a" */ -/* XXX: currently sparse produces "a" */ -/* Partially fixed: now it gives "\\a", which is a separate problem */ B(\a) #define C\ @@ -32,31 +47,21 @@ C #define D\ 1 /* And this should give D, since '\n' is removed and we get no whitespace */ -/* XXX: currently sparse produces 1 */ -/* Fixed */ D #define E '\\ a' /* This should give '\a' - with no warnings issued */ -/* XXX: currently sparse complains a lot and ends up producing a */ -/* Fixed */ E /* This should give nothing */ -/* XXX: currently sparse produces more junk */ -/* Fixed */ // junk \ more junk /* This should also give nothing */ -/* XXX: currently sparse produces / * comment * / */ -/* Fixed */ /\ * comment *\ / /* And this should complain since final newline should not be eaten by '\\' */ -/* XXX: currently sparse does not notice */ -/* Fixed */ \ diff --git a/usr/src/tools/smatch/src/validation/phase3/comments b/usr/src/tools/smatch/src/validation/preprocessor/phase3-comments.c index 8f51a307b0..7106b480a2 100644 --- a/usr/src/tools/smatch/src/validation/phase3/comments +++ b/usr/src/tools/smatch/src/validation/preprocessor/phase3-comments.c @@ -3,7 +3,15 @@ */ /* This should give nothing */ -/* XXX: currently sparse produces Y */ -/* Fixed */ #define X /* */ Y + +/* + * check-name: phase3-comments + * check-command: sparse -E $file + * + * check-output-start + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/predef-char-bit.c b/usr/src/tools/smatch/src/validation/preprocessor/predef-char-bit.c deleted file mode 100644 index fed0166e48..0000000000 --- a/usr/src/tools/smatch/src/validation/preprocessor/predef-char-bit.c +++ /dev/null @@ -1,16 +0,0 @@ -#define TEST_BIT(X, T) if (__ ## X ## _BIT__ != 8 * sizeof(T)) return 1 - -int test(void) -{ - TEST_BIT(CHAR, char); - - return 0; -} - -/* - * check-name: predefined __<type>_BIT__ - * check-command: test-linearize -Wno-decl $file - * check-output-ignore - * - * check-output-contains: ret\\..*\\$0 - */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/predef-llp64.c b/usr/src/tools/smatch/src/validation/preprocessor/predef-llp64.c new file mode 100644 index 0000000000..0a758690c4 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/predef-llp64.c @@ -0,0 +1,9 @@ +#include "predef.c" + +/* + * check-name: predefined macros for LLP64 + * check-command: test-linearize -Wno-decl -msize-llp64 $file + * check-output-ignore + * + * check-output-contains: ret\\..*\\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/predef-lp32.c b/usr/src/tools/smatch/src/validation/preprocessor/predef-lp32.c new file mode 100644 index 0000000000..8fa2846c26 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/predef-lp32.c @@ -0,0 +1,9 @@ +#include "predef.c" + +/* + * check-name: predefined macros for LP32 + * check-command: test-linearize -Wno-decl -m32 $file + * check-output-ignore + * + * check-output-contains: ret\\..*\\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/predef-lp64.c b/usr/src/tools/smatch/src/validation/preprocessor/predef-lp64.c new file mode 100644 index 0000000000..0173ca40d0 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/predef-lp64.c @@ -0,0 +1,9 @@ +#include "predef.c" + +/* + * check-name: predefined macros for LP64 + * check-command: test-linearize -Wno-decl -m64 $file + * check-output-ignore + * + * check-output-contains: ret\\..*\\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/predef-max.c b/usr/src/tools/smatch/src/validation/preprocessor/predef-max.c deleted file mode 100644 index ad4b7eaf1b..0000000000 --- a/usr/src/tools/smatch/src/validation/preprocessor/predef-max.c +++ /dev/null @@ -1,18 +0,0 @@ -#define TEST_MAX(X, Z) if (X != ((~ Z) >> 1)) return 1 - -int test_max(void) -{ - TEST_MAX(__INT_MAX__, 0U); - TEST_MAX(__LONG_MAX__, 0UL); - TEST_MAX(__LONG_LONG_MAX__, 0ULL); - - return 0; -} - -/* - * check-name: predefined __<type>_MAX__ - * check-command: test-linearize -Wno-decl $file - * check-output-ignore - * - * check-output-contains: ret\\..*\\$0 - */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/predef-sizeof.c b/usr/src/tools/smatch/src/validation/preprocessor/predef-sizeof.c deleted file mode 100644 index 12be2dd1dd..0000000000 --- a/usr/src/tools/smatch/src/validation/preprocessor/predef-sizeof.c +++ /dev/null @@ -1,25 +0,0 @@ -#define TEST(X, T) if (__SIZEOF_ ## X ## __ != sizeof(T)) return 1 - -int test_sizeof(void) -{ - TEST(SHORT, short); - TEST(INT, int); - TEST(LONG, long); - TEST(LONG_LONG, long long); - TEST(INT128, __int128); - TEST(SIZE_T, __SIZE_TYPE__); - TEST(POINTER, void*); - TEST(FLOAT, float); - TEST(DOUBLE, double); - TEST(LONG_DOUBLE, long double); - - return 0; -} - -/* - * check-name: predefined __SIZEOF_<type>__ - * check-command: test-linearize -Wno-decl $file - * check-output-ignore - * - * check-output-contains: ret\\..*\\$0 - */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/predef-unsigned.c b/usr/src/tools/smatch/src/validation/preprocessor/predef-unsigned.c new file mode 100644 index 0000000000..0ae4d165a1 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/predef-unsigned.c @@ -0,0 +1,9 @@ +#include "predef.c" + +/* + * check-name: predefined macros for -funsigned-char + * check-command: test-linearize -Wno-decl -funsigned-char $file + * check-output-ignore + * + * check-output-contains: ret\\..*\\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/preprocessor/predef.c b/usr/src/tools/smatch/src/validation/preprocessor/predef.c new file mode 100644 index 0000000000..5678acedf7 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/preprocessor/predef.c @@ -0,0 +1,57 @@ +#define BITS(T) (sizeof(T) * 8) +#define SIGN_BIT(T) (1ULL << (BITS(T) - 1)) +#define SMASK(T) (SIGN_BIT(T) - 1) +#define UMASK(T) (SIGN_BIT(T) | SMASK(T)) + +int test(void); +int test(void) +{ +#define TEST_BIT(X, T) if (__ ## X ## _BIT__ != BITS(T)) return 1 + TEST_BIT(CHAR, char); + +#define TEST_MAX(X, M) if (__ ## X ## _MAX__ != M) return 1 +#define TEST_SMAX(X, T) TEST_MAX(X, SMASK(T)) +#define TEST_UMAX(X, T) TEST_MAX(X, UMASK(T)) + TEST_SMAX(SCHAR, signed char); + TEST_SMAX(SHRT, short); + TEST_SMAX(INT, int); + TEST_SMAX(LONG, long); + TEST_SMAX(LONG_LONG, long long); + TEST_MAX( INT8, 0x7f); + TEST_MAX(UINT8, 0xffU); + TEST_MAX( INT16, 0x7fff); + TEST_MAX(UINT16, 0xffffU); + TEST_MAX( INT32, 0x7fffffff); + TEST_MAX(UINT32, 0xffffffffU); + TEST_MAX( INT64, 0x7fffffffffffffffLL); + TEST_MAX(UINT64, 0xffffffffffffffffULL); + TEST_SMAX(INTMAX, __INTMAX_TYPE__); + TEST_UMAX(UINTMAX, __UINTMAX_TYPE__); + TEST_SMAX(INTPTR, __INTPTR_TYPE__); + TEST_UMAX(UINTPTR, __UINTPTR_TYPE__); + TEST_SMAX(PTRDIFF, __PTRDIFF_TYPE__); + TEST_UMAX(SIZE, __SIZE_TYPE__); + +#define TEST_SIZEOF(X, T) if (__SIZEOF_ ## X ## __ != sizeof(T)) return 1 + TEST_SIZEOF(SHORT, short); + TEST_SIZEOF(INT, int); + TEST_SIZEOF(LONG, long); + TEST_SIZEOF(LONG_LONG, long long); + TEST_SIZEOF(INT128, __int128); + TEST_SIZEOF(PTRDIFF_T, __PTRDIFF_TYPE__); + TEST_SIZEOF(SIZE_T, __SIZE_TYPE__); + TEST_SIZEOF(POINTER, void*); + TEST_SIZEOF(FLOAT, float); + TEST_SIZEOF(DOUBLE, double); + TEST_SIZEOF(LONG_DOUBLE, long double); + + return 0; +} + +/* + * check-name: predefined macros: __SIZEOF_<type>__, ... + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * + * check-output-contains: ret\\..*\\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/ptr-inherit.c b/usr/src/tools/smatch/src/validation/ptr-inherit.c index 58524a7175..69e5a931b6 100644 --- a/usr/src/tools/smatch/src/validation/ptr-inherit.c +++ b/usr/src/tools/smatch/src/validation/ptr-inherit.c @@ -63,18 +63,18 @@ static void test_tls(void) * check-error-start ptr-inherit.c:12:19: warning: incorrect type in initializer (different modifiers) ptr-inherit.c:12:19: expected int *p -ptr-inherit.c:12:19: got int const *<noident> +ptr-inherit.c:12:19: got int const * ptr-inherit.c:18:19: warning: incorrect type in initializer (different modifiers) ptr-inherit.c:18:19: expected int *p -ptr-inherit.c:18:19: got int volatile *<noident> +ptr-inherit.c:18:19: got int volatile * ptr-inherit.c:24:19: warning: incorrect type in initializer (different modifiers) ptr-inherit.c:24:19: expected int *p -ptr-inherit.c:24:19: got int [noderef] *<noident> +ptr-inherit.c:24:19: got int [noderef] * ptr-inherit.c:30:19: warning: incorrect type in initializer (different base types) ptr-inherit.c:30:19: expected int *p -ptr-inherit.c:30:19: got restricted int *<noident> +ptr-inherit.c:30:19: got restricted int * ptr-inherit.c:36:19: warning: incorrect type in initializer (different address spaces) ptr-inherit.c:36:19: expected int *p -ptr-inherit.c:36:19: got int <asn:1>*<noident> +ptr-inherit.c:36:19: got int <asn:1> * * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/ptr-sub-blows.c b/usr/src/tools/smatch/src/validation/ptr-sub-blows.c new file mode 100644 index 0000000000..af3d79e7c5 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/ptr-sub-blows.c @@ -0,0 +1,23 @@ +static int ok(int *a, int *b) +{ + return a - b; +} + +struct s { + int a, b, c; +}; + +static int ko(struct s *a, struct s *b) +{ + return a - b; +} + +/* + * check-name: ptr-sub-blows + * check-command: sparse -Wptr-subtraction-blows $file + * + * check-error-start +ptr-sub-blows.c:12:18: warning: potentially expensive pointer subtraction +ptr-sub-blows.c:12:18: 'struct s' has a non-power-of-2 size: 12 + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/range-syntax.c b/usr/src/tools/smatch/src/validation/range-syntax.c new file mode 100644 index 0000000000..c43fff0e9f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/range-syntax.c @@ -0,0 +1,23 @@ + +static void ok(int a, int b, int c) +{ + __range__(a, 0, 8); + __range__(a, b, c); +} + +static void ko(int a, int b, int c) +{ + __range__ a, 0, 8; + __range__ a, b, c; +} + +/* + * check-name: range syntax + * + * check-error-start +range-syntax.c:10:19: error: Expected ( after __range__ statement +range-syntax.c:10:19: error: got a +range-syntax.c:11:19: error: Expected ( after __range__ statement +range-syntax.c:11:19: error: got a + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/repeat.h b/usr/src/tools/smatch/src/validation/repeat.h new file mode 100644 index 0000000000..83433b2a29 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/repeat.h @@ -0,0 +1,24 @@ +#define R0(P, S) P(S) +#define R1(P, S) R0(P,S##0) R0(P,S##1) +#define R2(P, S) R0(P,S##0) R0(P,S##1) R0(P,S##2) R0(P,S##3) +#define R3(P, S) R0(P,S##0) R0(P,S##1) R0(P,S##2) R0(P,S##3) R0(P,S##4) R0(P,S##5) R0(P,S##6) R0(P,S##7) +#define R4(P, S) R3(P,S##0) R3(P,S##1) +#define R5(P, S) R3(P,S##0) R3(P,S##1) R3(P,S##2) R3(P,S##3) +#define R6(P, S) R3(P,S##0) R3(P,S##1) R3(P,S##2) R3(P,S##3) R3(P,S##4) R3(P,S##5) R3(P,S##6) R3(P,S##7) +#define R7(P, S) R6(P,S##0) R6(P,S##1) +#define R8(P, S) R6(P,S##0) R6(P,S##1) R6(P,S##2) R6(P,S##3) +#define R9(P, S) R6(P,S##0) R6(P,S##1) R6(P,S##2) R6(P,S##3) R6(P,S##4) R6(P,S##5) R6(P,S##6) R6(P,S##7) +#define R10(P, S) R9(P,S##0) R9(P,S##1) +#define R11(P, S) R9(P,S##0) R9(P,S##1) R9(P,S##2) R9(P,S##3) +#define R12(P, S) R9(P,S##0) R9(P,S##1) R9(P,S##2) R9(P,S##3) R9(P,S##4) R9(P,S##5) R9(P,S##6) R9(P,S##7) +#define R13(P, S) R12(P,S##0) R12(P,S##1) +#define R14(P, S) R12(P,S##0) R12(P,S##1) R12(P,S##2) R12(P,S##3) +#define R15(P, S) R12(P,S##0) R12(P,S##1) R12(P,S##2) R12(P,S##3) R12(P,S##4) R12(P,S##5) R12(P,S##6) R12(P,S##7) +#define R16(P, S) R15(P,S##0) R15(P,S##1) +#define R17(P, S) R15(P,S##0) R15(P,S##1) R15(P,S##2) R15(P,S##3) +#define R18(P, S) R15(P,S##0) R15(P,S##1) R15(P,S##2) R15(P,S##3) R15(P,S##4) R15(P,S##5) R15(P,S##6) R15(P,S##7) +#define R19(P, S) R18(P,S##0) R18(P,S##1) +#define R20(P, S) R18(P,S##0) R18(P,S##1) R18(P,S##2) R18(P,S##3) + +#define REPEAT_(RN, P) RN(P,) +#define REPEAT2(N, P) REPEAT_(R##N,P) diff --git a/usr/src/tools/smatch/src/validation/reserved.c b/usr/src/tools/smatch/src/validation/reserved.c index 29554560b2..6a2163e9a2 100644 --- a/usr/src/tools/smatch/src/validation/reserved.c +++ b/usr/src/tools/smatch/src/validation/reserved.c @@ -81,7 +81,7 @@ static int (__builtin_va_list); /* * check-name: const et.al. are reserved identifiers - * check-error-start: + * check-error-start reserved.c:1:12: error: Trying to use reserved word 'auto' as identifier reserved.c:2:12: error: Trying to use reserved word 'break' as identifier reserved.c:3:12: error: Trying to use reserved word 'case' as identifier @@ -154,5 +154,5 @@ reserved.c:77:12: error: Trying to use reserved word '__builtin_ms_va_list' as i reserved.c:78:12: error: Trying to use reserved word '__builtin_offsetof' as identifier reserved.c:79:12: error: Trying to use reserved word '__builtin_types_compatible_p' as identifier reserved.c:80:12: error: Trying to use reserved word '__builtin_va_list' as identifier - * check-error-end: + * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/restrict.c b/usr/src/tools/smatch/src/validation/restrict.c new file mode 100644 index 0000000000..92bfdae883 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/restrict.c @@ -0,0 +1,93 @@ +void f00(void *restrict dst); +void f01(void *restrict *dst); +void f02(void *restrict *dst); +void f03(void *restrict *dst); + +void *restrict rp; +void * up; + +void f00(void *dst) { } /* check-should-pass */ +void f01(typeof(&rp) dst) { } /* check-should-pass */ +void f02(void **dst) { } /* check-should-fail */ +void f03(typeof(&up) dst) { } /* check-should-fail */ + +void foo(void) +{ + rp = up; /* check-should-pass */ + up = rp; /* check-should-pass */ +} + +void ref(void) +{ + void *const qp; + void * up; + extern void *const *pqp; + extern void **pup; + + pqp = &qp; /* check-should-pass */ + pqp = &up; /* check-should-pass */ + pqp = pup; + + pup = &up; /* check-should-pass */ + + pup = &qp; /* check-should-fail */ + pup = pqp; /* check-should-fail */ +} + +void bar(void) +{ + extern void *restrict *prp; + extern void **pup; + + prp = &rp; /* check-should-pass */ + prp = &up; /* check-should-pass */ + prp = pup; + + pup = &up; /* check-should-pass */ + + pup = &rp; /* check-should-fail */ + pup = prp; /* check-should-fail */ +} + +void baz(void) +{ + extern typeof(&rp) prp; + extern typeof(&up) pup; + + prp = &rp; /* check-should-pass */ + prp = &up; /* check-should-pass */ + prp = pup; + + pup = &up; /* check-should-pass */ + + pup = &rp; /* check-should-fail */ + pup = prp; /* check-should-fail */ +} + +/* + * check-name: restrict qualifier + * check-command: sparse -Wno-decl $file; + * + * check-error-start +restrict.c:11:6: error: symbol 'f02' redeclared with different type (originally declared at restrict.c:3) - incompatible argument 1 (different modifiers) +restrict.c:12:6: error: symbol 'f03' redeclared with different type (originally declared at restrict.c:4) - incompatible argument 1 (different modifiers) +restrict.c:33:13: warning: incorrect type in assignment (different modifiers) +restrict.c:33:13: expected void **extern [assigned] pup +restrict.c:33:13: got void *const * +restrict.c:34:13: warning: incorrect type in assignment (different modifiers) +restrict.c:34:13: expected void **extern [assigned] pup +restrict.c:34:13: got void *const *extern [assigned] pqp +restrict.c:48:13: warning: incorrect type in assignment (different modifiers) +restrict.c:48:13: expected void **extern [assigned] pup +restrict.c:48:13: got void *restrict * +restrict.c:49:13: warning: incorrect type in assignment (different modifiers) +restrict.c:49:13: expected void **extern [assigned] pup +restrict.c:49:13: got void *restrict *extern [assigned] prp +restrict.c:63:13: warning: incorrect type in assignment (different modifiers) +restrict.c:63:13: expected void **extern [assigned] pup +restrict.c:63:13: got void *restrict * +restrict.c:64:13: warning: incorrect type in assignment (different modifiers) +restrict.c:64:13: expected void **extern [assigned] pup +restrict.c:64:13: got void *restrict *extern [assigned] prp + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/self-quote-args.c b/usr/src/tools/smatch/src/validation/self-quote-args.c new file mode 100644 index 0000000000..be9873d233 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/self-quote-args.c @@ -0,0 +1,7 @@ +/* + * check-name: self-quote-args + * check-description: This is testing that the test-suite + * respect the quoting of the command's arguments. + * check-command: sparse '-foption with-spaces' empty-file + * check-output-ignore + */ diff --git a/usr/src/tools/smatch/src/validation/shift-negative.c b/usr/src/tools/smatch/src/validation/shift-negative.c new file mode 100644 index 0000000000..fff5cf123d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/shift-negative.c @@ -0,0 +1,17 @@ +unsigned int fn1(unsigned int a) { return a >> -1; } +unsigned int fn2(unsigned int a) { return a >> ~0; } + +unsigned int fo1(unsigned int a) { return a >> ((a & 0) | -1); } +unsigned int fo2(unsigned int a) { return a >> ((a & 0) ^ ~0); } + +/* + * check-name: shift-negative + * check-command: sparse -Wno-decl $file + * + * check-error-start +shift-negative.c:1:45: warning: shift count is negative (-1) +shift-negative.c:2:45: warning: shift count is negative (-1) +shift-negative.c:4:59: warning: shift count is negative (-1) +shift-negative.c:5:59: warning: shift count is negative (-1) + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/shift-undef-long.c b/usr/src/tools/smatch/src/validation/shift-undef-long.c new file mode 100644 index 0000000000..326267436e --- /dev/null +++ b/usr/src/tools/smatch/src/validation/shift-undef-long.c @@ -0,0 +1,21 @@ +static unsigned very_big_shift(unsigned int a) +{ + unsigned r = 0; + r |= a << (0ULL ^ ~0U); + r |= a << ((( signed long long) ~0U) + 1); + r |= a << (((unsigned long long) ~0U) + 1); + r |= a << (~((unsigned long long) ~0U)); + return r; +} + +/* + * check-name: shift-undef-long + * check-command: sparse -m64 $file + * + * check-error-start +shift-undef-long.c:4:16: warning: shift too big (4294967295) for type unsigned int +shift-undef-long.c:5:16: warning: shift too big (4294967296) for type unsigned int +shift-undef-long.c:6:16: warning: shift too big (4294967296) for type unsigned int +shift-undef-long.c:7:16: warning: shift count is negative (-4294967296) + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/shift-undef.c b/usr/src/tools/smatch/src/validation/shift-undef.c new file mode 100644 index 0000000000..4e94fa23d1 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/shift-undef.c @@ -0,0 +1,164 @@ +int simple(int s, unsigned int u, int p) +{ + s = s >> 100; + u = u >> 101; + u = u << 102; + s = s >> -1; + u = u >> -2; + u = u << -3; + if (0) return s >> 103; + if (0) return u >> 104; + if (0) return u << 105; + if (0) return s >> -4; + if (0) return u >> -5; + if (0) return u << -6; + if (p && 0) return s >> 106; + if (p && 0) return u >> 107; + if (p && 0) return u << 108; + if (p && 0) return s >> -7; + if (p && 0) return u >> -8; + if (p && 0) return u << -9; + s = s >> ((p & 0) + 109); u ^= p; // reloaded because now == 0 + u = u >> ((p & 0) + 110); u ^= p; // reloaded because now == 0 + u = u << ((p & 0) + 111); u ^= p; // reloaded because now == 0 + s = s >> ((p & 0) + -10); + u = u >> ((p & 0) + -11); u ^= p; // reloaded because now == 0 + u = u << ((p & 0) + -12); u ^= p; // reloaded because now == 0 + return s + u; +} + +int compound(int s, unsigned int u, int p) +{ + s >>= 100; + u >>= 101; + u <<= 102; + s >>= -1; + u >>= -2; + u <<= -3; + if (0) return s >>= 103; + if (0) return u >>= 104; + if (0) return u <<= 105; + if (0) return s >>= -4; + if (0) return u >>= -5; + if (0) return u <<= -6; + if (p && 0) return s >>= 106; + if (p && 0) return u >>= 107; + if (p && 0) return u <<= 108; + if (p && 0) return s >>= -7; + if (p && 0) return u >>= -8; + if (p && 0) return u <<= -9; + s >>= ((p & 0) + 109); u ^= p; // reloaded because now == 0 + u >>= ((p & 0) + 110); u ^= p; // reloaded because now == 0 + u <<= ((p & 0) + 111); u ^= p; // reloaded because now == 0 + s >>= ((p & 0) + -10); + u >>= ((p & 0) + -11); u ^= p; // reloaded because now == 0 + u <<= ((p & 0) + -12); u ^= p; // reloaded because now == 0 + return s + u; +} + +int ok(int s, unsigned int u, int p) +{ + // GCC doesn't warn on these + if (0 && (s >> 100)) return 0; + if (0 && (u >> 101)) return 0; + if (0 && (u << 102)) return 0; + if (0 && (s >> -1)) return 0; + if (0 && (u >> -2)) return 0; + if (0 && (u << -3)) return 0; + if (0 && (s >>= 103)) return 0; + if (0 && (u >>= 104)) return 0; + if (0 && (u <<= 105)) return 0; + if (0 && (s >>= -4)) return 0; + if (0 && (u >>= -5)) return 0; + if (0 && (u <<= -6)) return 0; + return 1; +} + +struct bf { + unsigned int u:8; + int s:8; +}; + +int bf(struct bf *p) +{ + unsigned int r = 0; + r += p->s << 8; + r += p->s >> 8; + r += p->u >> 8; + return r; +} + +/* + * The following is used in the kernel at several places + * It shouldn't emit any warnings. + */ +typedef unsigned long long u64; +typedef unsigned int u32; + +extern void hw_w32x2(u32 hi, u32 lo); + +inline void hw_w64(u64 val) +{ + hw_w32x2(val >> 32, (u32) val); +} + +void hw_write(u32 val) +{ + hw_w64(val); +} + +/* + * check-name: shift too big or negative + * check-command: sparse -Wno-decl $file + * + * check-error-start +shift-undef.c:3:15: warning: shift too big (100) for type int +shift-undef.c:4:15: warning: shift too big (101) for type unsigned int +shift-undef.c:5:15: warning: shift too big (102) for type unsigned int +shift-undef.c:6:15: warning: shift count is negative (-1) +shift-undef.c:7:15: warning: shift count is negative (-2) +shift-undef.c:8:15: warning: shift count is negative (-3) +shift-undef.c:9:25: warning: shift too big (103) for type int +shift-undef.c:10:25: warning: shift too big (104) for type unsigned int +shift-undef.c:11:25: warning: shift too big (105) for type unsigned int +shift-undef.c:12:25: warning: shift count is negative (-4) +shift-undef.c:13:25: warning: shift count is negative (-5) +shift-undef.c:14:25: warning: shift count is negative (-6) +shift-undef.c:15:30: warning: shift too big (106) for type int +shift-undef.c:16:30: warning: shift too big (107) for type unsigned int +shift-undef.c:17:30: warning: shift too big (108) for type unsigned int +shift-undef.c:18:30: warning: shift count is negative (-7) +shift-undef.c:19:30: warning: shift count is negative (-8) +shift-undef.c:20:30: warning: shift count is negative (-9) +shift-undef.c:21:29: warning: shift too big (109) for type int +shift-undef.c:22:29: warning: shift too big (110) for type unsigned int +shift-undef.c:23:29: warning: shift too big (111) for type unsigned int +shift-undef.c:24:29: warning: shift count is negative (-10) +shift-undef.c:25:29: warning: shift count is negative (-11) +shift-undef.c:26:29: warning: shift count is negative (-12) +shift-undef.c:32:11: warning: shift too big (100) for type int +shift-undef.c:33:11: warning: shift too big (101) for type unsigned int +shift-undef.c:34:11: warning: shift too big (102) for type unsigned int +shift-undef.c:35:11: warning: shift count is negative (-1) +shift-undef.c:36:11: warning: shift count is negative (-2) +shift-undef.c:37:11: warning: shift count is negative (-3) +shift-undef.c:38:25: warning: shift too big (103) for type int +shift-undef.c:39:25: warning: shift too big (104) for type unsigned int +shift-undef.c:40:25: warning: shift too big (105) for type unsigned int +shift-undef.c:41:25: warning: shift count is negative (-4) +shift-undef.c:42:25: warning: shift count is negative (-5) +shift-undef.c:43:25: warning: shift count is negative (-6) +shift-undef.c:44:30: warning: shift too big (106) for type int +shift-undef.c:45:30: warning: shift too big (107) for type unsigned int +shift-undef.c:46:30: warning: shift too big (108) for type unsigned int +shift-undef.c:47:30: warning: shift count is negative (-7) +shift-undef.c:48:30: warning: shift count is negative (-8) +shift-undef.c:49:30: warning: shift count is negative (-9) +shift-undef.c:50:26: warning: shift too big (109) for type int +shift-undef.c:51:26: warning: shift too big (110) for type int +shift-undef.c:52:26: warning: shift too big (111) for type int +shift-undef.c:53:26: warning: shift count is negative (-10) +shift-undef.c:54:26: warning: shift count is negative (-11) +shift-undef.c:55:26: warning: shift count is negative (-12) + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/sizeof-bool.c b/usr/src/tools/smatch/src/validation/sizeof-bool.c index 05e76a44e8..9f21d1c65e 100644 --- a/usr/src/tools/smatch/src/validation/sizeof-bool.c +++ b/usr/src/tools/smatch/src/validation/sizeof-bool.c @@ -8,6 +8,6 @@ static int a(void) * number of bytes * check-command: sparse -Wsizeof-bool $file * check-error-start -sizeof-bool.c:3:16: warning: expression using sizeof bool +sizeof-bool.c:3:16: warning: expression using sizeof _Bool * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/sizeof-builtin.c b/usr/src/tools/smatch/src/validation/sizeof-builtin.c new file mode 100644 index 0000000000..7123e4deb5 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/sizeof-builtin.c @@ -0,0 +1,15 @@ +int test(void); +int test(void) +{ + return sizeof &__builtin_trap; +} + +/* + * check-name: sizeof-builtin + * check-command: sparse -Wno-decl $file + * check-known-to-fail + * + * check-error-start +sizeof-function.c:4:16: error: expression using addressof on a builtin function + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/sizeof-function.c b/usr/src/tools/smatch/src/validation/sizeof-function.c new file mode 100644 index 0000000000..8ff67f2149 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/sizeof-function.c @@ -0,0 +1,49 @@ +extern int fun(void); +extern int (*ptr)(void); + +static inline int inl(int *a) +{ + return *a + 1; +} + + +int test(void); +int test(void) +{ + unsigned int s = 0; + + // OK + s += sizeof &fun; + s += sizeof ptr; + s += sizeof &ptr; + s += sizeof &inl; + + // KO + s += sizeof fun; + s += sizeof *fun; + + s += sizeof *ptr; + + s += sizeof inl; + s += sizeof *inl; + + s += sizeof __builtin_trap; + s += sizeof *__builtin_trap; + + return s; +} + +/* + * check-name: sizeof-function + * check-command: sparse -Wpointer-arith -Wno-decl $file + * + * check-error-start +sizeof-function.c:22:14: warning: expression using sizeof on a function +sizeof-function.c:23:14: warning: expression using sizeof on a function +sizeof-function.c:25:14: warning: expression using sizeof on a function +sizeof-function.c:27:14: warning: expression using sizeof on a function +sizeof-function.c:28:14: warning: expression using sizeof on a function +sizeof-function.c:30:14: warning: expression using sizeof on a function +sizeof-function.c:31:14: warning: expression using sizeof on a function + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/sizeof-incomplete-type.c b/usr/src/tools/smatch/src/validation/sizeof-incomplete-type.c new file mode 100644 index 0000000000..9fba00b38d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/sizeof-incomplete-type.c @@ -0,0 +1,27 @@ +struct s { + char a; + char b[sizeof(struct s)]; + char c; + char d[sizeof(struct s)]; + int j:sizeof(struct s); +}; + +static int array[] = { + [0] = 0, + [sizeof(array)] = 1, + [2] = 0, + [sizeof(array)] = 2, +}; + +/* + * check-name: sizeof incomplete type + * + * check-known-to-fail + * check-error-start +sizeof-incomplete-type.c:3:16: error: invalid application of 'sizeof' to incomplete type 'struct s' +sizeof-incomplete-type.c:5:16: error: invalid application of 'sizeof' to incomplete type 'struct s' +sizeof-incomplete-type.c:6:16: error: invalid application of 'sizeof' to incomplete type 'struct s' +sizeof-incomplete-type.c:11:17: error: invalid application of 'sizeof' to incomplete type 'int[]' +sizeof-incomplete-type.c:13:17: error: invalid application of 'sizeof' to incomplete type 'int[]' + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/sm_compare18.c b/usr/src/tools/smatch/src/validation/sm_compare18.c new file mode 100644 index 0000000000..ccade32a7d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/sm_compare18.c @@ -0,0 +1,24 @@ +#include "check_debug.h" + +int a, b; +static int options_write(void) +{ + if (a == b) + return; + + if (a < 10) + return; + if (b > 10) + return; + __smatch_compare(a, b); +} + + +/* + * check-name: smatch compare #18 + * check-command: smatch -I.. sm_compare18.c + * + * check-output-start +sm_compare18.c:13 options_write() a > b + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/storage-struct-member.c b/usr/src/tools/smatch/src/validation/storage-struct-member.c new file mode 100644 index 0000000000..6bd958abcb --- /dev/null +++ b/usr/src/tools/smatch/src/validation/storage-struct-member.c @@ -0,0 +1,20 @@ +int foo(a) + register int a; +{ + return a; +} + +struct s { + register int a; +}; + +/* + * check-name: storage in struct member + * check-command: sparse -Wno-decl $file + * + * check-known-to-fail + * check-error-start +storage-struct-member.c:2:9: warning: non-ANSI definition of function 'foo' +storage-struct-member.c:8:9: error: storage specifier in structure definition' + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/struct-as.c b/usr/src/tools/smatch/src/validation/struct-as.c index f31f7c9664..23bca897db 100644 --- a/usr/src/tools/smatch/src/validation/struct-as.c +++ b/usr/src/tools/smatch/src/validation/struct-as.c @@ -12,7 +12,7 @@ extern int test(int __user *ip); static int broken(struct hello __user *sp) { - test(&sp->a); + return test(&sp->a); } /* * check-name: Address space of a struct member diff --git a/usr/src/tools/smatch/src/validation/switch-long.c b/usr/src/tools/smatch/src/validation/switch-long.c new file mode 100644 index 0000000000..5bfdb43975 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/switch-long.c @@ -0,0 +1,47 @@ +void def(void); +void r0(void); +void r1(void); + +void sw_long(long long a) +{ + switch (a) { + case 0: return r0(); + case 1LL << 00: return r1(); + case 1LL << 32: return r1(); + } + + return def(); +} + +/* + * check-name: switch-long + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +sw_long: +.L0: + <entry-point> + switch.64 %arg1, 0 -> .L2, 1 -> .L3, 4294967296 -> .L4, default -> .L1 + +.L2: + call r0 + br .L5 + +.L3: + call r1 + br .L5 + +.L4: + call r1 + br .L5 + +.L1: + call def + br .L5 + +.L5: + ret + + + * check-output-end + */ diff --git a/usr/src/tools/smatch/src/validation/test-be.c b/usr/src/tools/smatch/src/validation/test-be.c deleted file mode 100644 index deda3cc14f..0000000000 --- a/usr/src/tools/smatch/src/validation/test-be.c +++ /dev/null @@ -1,46 +0,0 @@ -int printf(char *c, ...); -void exit(int c); - -#undef PRINT_OUTPUTS - -static void test_func_args(int x, int y) -{ - if (x == y) - exit(1); -} - -static int binop_s32(int x, int y) -{ - int a; - - a = a + x; - a = a / y; - a = a * x; - a = a - y; - - return a; -} - -static void test_binops(void) -{ - int tmp_s32 = binop_s32(987123, 234); - -#ifdef PRINT_OUTPUTS - printf("binop_s32(987123, 234) == %d\n", tmp_s32); -#else - if (tmp_s32 != -1470599007) - exit(2); -#endif -} - -int main (int argc, char *argv[]) -{ - test_func_args(1, 2); - test_binops(); - - return 0; -} - -/* - * check-name: binary operations - */ diff --git a/usr/src/tools/smatch/src/validation/test-suite b/usr/src/tools/smatch/src/validation/test-suite index 5e10942750..64a3e08fb4 100755 --- a/usr/src/tools/smatch/src/validation/test-suite +++ b/usr/src/tools/smatch/src/validation/test-suite @@ -6,11 +6,12 @@ cd $(dirname "$0") default_path=".." default_cmd="sparse \$file" -tests_list=`find . -name '*.c' | sed -e 's#^\./\(.*\)#\1#' | sort` +default_args="$SPARSE_TEST_ARGS" +tests_list="" prog_name=`basename $0` if [ ! -x "$default_path/sparse-llvm" ]; then - disabled_cmds="sparsec sparsei sparse-llvm" + disabled_cmds="sparsec sparsei sparse-llvm sparse-llvm-dis" fi # flags: @@ -31,7 +32,36 @@ known_ko_tests=0 # defaults to not verbose [ -z "$V" ] && V=0 -[ $V -eq 0 ] && quiet=1 || quiet=0 +vquiet="" +quiet=0 +abort=0 + + +## +# verbose(string) - prints string if we are in verbose mode +verbose() +{ + [ "$V" -eq "1" ] && echo " $1" + return 0 +} + +## +# warning(string) - prints a warning +warning() +{ + [ "$quiet" -ne 1 ] && echo "warning: $1" + return 0 +} + +## +# error(string[, die]) - prints an error and exits with value die if given +error() +{ + [ "$quiet" -ne 1 ] && echo "error: $1" + [ -n "$2" ] && exit $2 + return 0 +} + ## # get_tag_value(file) - get the 'check-<...>' tags & values @@ -47,6 +77,10 @@ get_tag_value() check_output_contains=0 check_output_excludes=0 check_output_pattern=0 + check_arch_ignore="" + check_arch_only="" + check_assert="" + check_cpp_if="" lines=$(grep 'check-[a-z-]*' $1 | \ sed -e 's/^.*\(check-[a-z-]*:*\) *\(.*\)$/\1 \2/') @@ -65,7 +99,25 @@ get_tag_value() check-output-ignore) check_output_ignore=1 ;; check-output-contains:) check_output_contains=1 ;; check-output-excludes:) check_output_excludes=1 ;; - check-output-pattern-) check_output_pattern=1 ;; + check-output-pattern) check_output_pattern=1 ;; + check-arch-ignore:) arch=$(uname -m) + check_arch_ignore="$val" ;; + check-arch-only:) arch=$(uname -m) + check_arch_only="$val" ;; + check-assert:) check_assert="$val" ;; + check-cpp-if:) check_cpp_if="$val" ;; + + check-description:) ;; # ignore + check-note:) ;; # ignore + check-warning:) ;; # ignore + check-error-start) ;; # ignore + check-error-end) ;; # ignore + check-output-start) ;; # ignore + check-output-end) ;; # ignore + check-should-pass) ;; # ignore, unused annotation + check-should-fail) ;; # ignore, unused annotation + check-should-warn) ;; # ignore, unused annotation + check-*) error "$1: unknown tag '$tag'" 1 ;; esac done << EOT $lines @@ -80,11 +132,13 @@ has_patterns() patt="$2" ofile="$3" cmp="$4" + msg="$5" grep "$patt:" "$ifile" | \ sed -e "s/^.*$patt: *\(.*\)$/\1/" | \ while read val; do grep -s -q "$val" "$ofile" if [ "$?" $cmp 0 ]; then + error " Pattern '$val' unexpectedly $msg" return 1 fi done @@ -99,7 +153,7 @@ has_patterns() # returns 0 if all present, 1 otherwise has_each_patterns() { - has_patterns "$1" "$2" "$3" -ne + has_patterns "$1" "$2" "$4" -ne "$3" } ## @@ -109,24 +163,41 @@ has_each_patterns() # returns 1 if any present, 0 otherwise has_none_patterns() { - has_patterns "$1" "$2" "$3" -eq + has_patterns "$1" "$2" "$4" -eq "$3" } ## -# nbr_patterns(ifile tag ofile) - does ofile contains the +# minmax_patterns(ifile tag ofile) - does ofile contains the # the patterns given by ifile's tags # the right number of time? -nbr_patterns() +minmax_patterns() { ifile="$1" patt="$2" ofile="$3" - grep "$patt-[0-9][0-9]*-times:" "$ifile" | \ - sed -e "s/^.*$patt-\([0-9][0-9]*\)-times: *\(.*\)/\1 \2/" | \ - while read nbr pat; do + grep "$patt([0-9-]*\(, *\)*[0-9-]*):" "$ifile" | \ + sed -e "s/^.*$patt(\([0-9]*\)): *\(.*\)/\1 eq \2/" \ + -e "s/^.*$patt(\([0-9-]*\), *\([0-9-]*\)): *\(.*\)/\1 \2 \3/" | \ + while read min max pat; do n=$(grep -s "$pat" "$ofile" | wc -l) - if [ "$n" -ne "$nbr" ]; then + if [ "$max" = "eq" ]; then + if [ "$n" -ne "$min" ]; then + error " Pattern '$pat' expected $min times but got $n times" return 1 + fi + continue + fi + if [ "$min" != '-' ]; then + if [ "$n" -lt "$min" ]; then + error " Pattern '$pat' expected min $min times but got $n times" + return 1 + fi + fi + if [ "$max" != '-' ]; then + if [ "$n" -gt "$max" ]; then + error " Pattern '$pat' expected max $max times but got $n times" + return 1 + fi fi done @@ -134,33 +205,48 @@ nbr_patterns() } ## -# verbose(string) - prints string if we are in verbose mode -verbose() +# arg_file(filename) - checks if filename exists +arg_file() { - [ "$V" -eq "1" ] && echo " $1" + [ -z "$1" ] && { + do_usage + exit 1 + } + [ -e "$1" ] || { + error "Can't open file $1" + exit 1 + } return 0 } -## -# error(string[, die]) - prints an error and exits with value die if given -error() -{ - [ "$quiet" -ne 1 ] && echo "error: $1" - [ -n "$2" ] && exit $2 - return 0 -} +## do_usage() { echo "$prog_name - a tiny automatic testing script" -echo "Usage: $prog_name [command] [command arguments]" +echo "Usage: $prog_name [option(s)] [command] [arguments]" +echo +echo "options:" +echo " -a|--abort Abort the tests as soon as one fails." +echo " -q|--quiet Be extra quiet while running the tests." +echo " --args='...' Add these options to the test command." echo echo "commands:" -echo " none runs the whole test suite" -echo " single file runs the test in 'file'" -echo " format file [name [cmd]] helps writing a new test case using cmd" +echo " [file ...] Runs the test suite on the given file(s)." +echo " If a directory is given, run only those files." +echo " If no file is given, run the whole testsuite." +echo " single file Run the test in 'file'." +echo " format file [name [cmd]] Help writing a new test case using cmd." echo -echo " help prints usage" +echo " [command] help Print usage." +} + +disable() +{ + disabled_tests=$(($disabled_tests + 1)) + if [ -z "$vquiet" ]; then + echo " SKIP $1 ($2)" + fi } ## @@ -177,13 +263,14 @@ do_test() { test_failed=0 file="$1" + quiet=0 get_tag_value $file # can this test be handled by test-suite ? # (it has to have a check-name key in it) if [ "$check_name" = "" ]; then - echo "warning: test '$file' unhandled" + warning "$file: test unhandled" unhandled_tests=$(($unhandled_tests + 1)) return 2 fi @@ -199,37 +286,73 @@ do_test() base_cmd=$1 for i in $disabled_cmds; do if [ "$i" = "$base_cmd" ] ; then - disabled_tests=$(($disabled_tests + 1)) - echo " DISABLE $test_name ($file)" + disable "$test_name" "$file" return 3 fi done + if [ "$check_arch_ignore" != "" ]; then + if echo $arch | egrep -q -w "$check_arch_ignore"; then + disable "$test_name" "$file" + return 3 + fi + fi + if [ "$check_arch_only" != "" ]; then + if ! (echo $arch | egrep -q -w "$check_arch_only"); then + disable "$test_name" "$file" + return 3 + fi + fi + if [ "$check_assert" != "" ]; then + res=$(../sparse - 2>&1 >/dev/null <<- EOF + _Static_assert($check_assert, "$check_assert"); + EOF + ) + if [ "$res" != "" ]; then + disable "$test_name" "$file" + return 3 + fi + fi + if [ "$check_cpp_if" != "" ]; then + res=$(../sparse -E - 2>/dev/null <<- EOF + #if !($check_cpp_if) + fail + #endif + EOF + ) + if [ "$res" != "" ]; then + disable "$test_name" "$file" + return 3 + fi + fi - cmd=`eval echo $default_path/$check_command` - - echo " TEST $test_name ($file)" + if [ -z "$vquiet" ]; then + echo " TEST $test_name ($file)" + fi - verbose "Using command : $cmd" + verbose "Using command : $(echo "$@")" # grab the expected exit value expected_exit_value=$check_exit_value verbose "Expecting exit value: $expected_exit_value" # do we want a timeout? + pre_cmd="" if [ $check_timeout -ne 0 ]; then - cmd="timeout -k 1s $check_timeout $cmd" + pre_cmd="timeout -k 1s $check_timeout" fi + shift + # launch the test command and # grab the actual output & exit value - $cmd 1> $file.output.got 2> $file.error.got + eval $pre_cmd $default_path/$base_cmd $default_args "$@" \ + 1> $file.output.got 2> $file.error.got actual_exit_value=$? must_fail=$check_known_to_fail - quiet=0 [ $must_fail -eq 1 ] && [ $V -eq 0 ] && quiet=1 known_ko_tests=$(($known_ko_tests + $must_fail)) - for stream in output error; do + for stream in error output; do eval ignore=\$check_${stream}_ignore [ $ignore -eq 1 ] && continue @@ -254,37 +377,45 @@ do_test() # verify the 'check-output-contains/excludes' tags if [ $check_output_contains -eq 1 ]; then - has_each_patterns "$file" 'check-output-contains' $file.output.got + has_each_patterns "$file" 'check-output-contains' absent $file.output.got if [ "$?" -ne "0" ]; then - error "Actual output doesn't contain some of the expected patterns." test_failed=1 fi fi if [ $check_output_excludes -eq 1 ]; then - has_none_patterns "$file" 'check-output-excludes' $file.output.got + has_none_patterns "$file" 'check-output-excludes' present $file.output.got if [ "$?" -ne "0" ]; then - error "Actual output contains some patterns which are not expected." test_failed=1 fi fi if [ $check_output_pattern -eq 1 ]; then - # verify the 'check-output-pattern-X-times' tags - nbr_patterns "$file" 'check-output-pattern' $file.output.got + # verify the 'check-output-pattern(...)' tags + minmax_patterns "$file" 'check-output-pattern' $file.output.got if [ "$?" -ne "0" ]; then - error "Actual output doesn't contain the pattern the expected number." test_failed=1 fi fi - [ "$test_failed" -eq "$must_fail" ] || failed=1 - if [ "$must_fail" -eq "1" ]; then if [ "$test_failed" -eq "1" ]; then - echo "info: test '$file' is known to fail" + [ -z "$vquiet" ] && \ + echo "info: XFAIL: test '$file' is known to fail" else - echo "error: test '$file' is known to fail but succeed!" - test_failed=1 + echo "error: XPASS: test '$file' is known to fail but succeed!" fi + else + if [ "$test_failed" -eq "1" ]; then + echo "error: FAIL: test '$file' failed" + else + [ "$V" -ne "0" ] && \ + echo "info: PASS: test '$file' passed" + fi + fi + + if [ "$test_failed" -ne "$must_fail" ]; then + [ $abort -eq 1 ] && exit 1 + test_failed=1 + failed=1 fi if [ "$test_failed" -eq "1" ]; then @@ -302,37 +433,80 @@ do_test_suite() do_test "$i" done + OK=OK + [ $failed -eq 0 ] || OK=KO + # prints some numbers tests_nr=$(($ok_tests + $ko_tests)) - echo -n "Out of $tests_nr tests, $ok_tests passed, $ko_tests failed" - echo " ($known_ko_tests of them are known to fail)" + echo "$OK: out of $tests_nr tests, $ok_tests passed, $ko_tests failed" + if [ "$known_ko_tests" -ne 0 ]; then + echo " $known_ko_tests of them are known to fail" + fi if [ "$unhandled_tests" -ne "0" ]; then - echo "$unhandled_tests tests could not be handled by $prog_name" + echo " $unhandled_tests tests could not be handled by $prog_name" fi if [ "$disabled_tests" -ne "0" ]; then - echo "$disabled_tests tests were disabled" + echo " $disabled_tests tests were disabled" fi } ## -# do_format(file[, name[, cmd]]) - helps a test writer to format test-suite tags +do_format_help() { +echo "Usage: $prog_name [option(s)] [--]format file [name [cmd]]" +echo +echo "options:" +echo " -a append the created test to the input file" +echo " -f write a test known to fail" +echo " -l write a test for linearized code" +echo +echo "argument(s):" +echo " file file containing the test case(s)" +echo " name name for the test case (defaults to file)" +echo " cmd command to be used (defaults to 'sparse \$file')" +} + +## +# do_format([options,] file[, name[, cmd]]) - helps a test writer to format test-suite tags do_format() { - if [ -z "$2" ]; then - fname="$1" - fcmd=$default_cmd - elif [ -z "$3" ]; then - fname="$2" - fcmd=$default_cmd - else - fname="$2" - fcmd="$3" - fi + def_cmd="$default_cmd" + append=0 + linear=0 + fail=0 + + while [ $# -gt 1 ] ; do + case "$1" in + -a) + append=1 ;; + -f) + fail=1 ;; + -l) + def_cmd='test-linearize -Wno-decl $file' + linear=1 ;; + help|-*) + do_format_help + return 0 + ;; + *) break ;; + esac + shift + continue + done + + arg_file "$1" || return 1 + file="$1" + fname="$2" + [ -z "$fname" ] && fname="$(basename "$1" .c)" + fcmd="$3" + [ -z "$fcmd" ] && fcmd="$def_cmd" + cmd=`eval echo $default_path/$fcmd` $cmd 1> $file.output.got 2> $file.error.got fexit_value=$? + [ $append != 0 ] && exec >> $file cat <<_EOF + /* * check-name: $fname _EOF @@ -342,6 +516,15 @@ _EOF if [ "$fexit_value" -ne "0" ]; then echo " * check-exit-value: $fexit_value" fi + if [ $fail != 0 ]; then + echo " * check-known-to-fail" + fi + if [ $linear != 0 ]; then + echo ' *' + echo ' * check-output-ignore' + echo ' * check-output-contains: xyz\\\\.' + echo ' * check-output-excludes: \\\\.' + fi for stream in output error; do if [ -s "$file.$stream.got" ]; then echo " *" @@ -354,26 +537,23 @@ _EOF return 0 } -## -# arg_file(filename) - checks if filename exists -arg_file() -{ - [ -z "$1" ] && { - do_usage - exit 1 - } - [ -e "$1" ] || { - error "Can't open file $1" - exit 1 - } - return 0 -} +## allow flags from environment +set -- $SPARSE_TEST_FLAGS "$@" -case "$1" in - '') - do_test_suite +## process the flags +while [ "$#" -gt "0" ]; do + case "$1" in + -a|--abort) + abort=1 + ;; + -q|--quiet) + vquiet=1 ;; - single) + --args=*) + default_args="${1#--args=}"; + ;; + + single|--single) arg_file "$2" do_test "$2" case "$?" in @@ -381,16 +561,36 @@ case "$1" in 1) echo "$2 failed !";; 2) echo "$2 can't be handled by $prog_name";; esac + exit $failed ;; - format) - arg_file "$2" - do_format "$2" "$3" "$4" + format|--format) + shift + do_format "$@" + exit 0 ;; - help | *) + help) do_usage exit 1 ;; -esac + *.c|*.cdoc) + tests_list="$tests_list $1" + ;; + *) + if [ ! -d "$1" ]; then + do_usage + exit 1 + fi + tests_list="$tests_list $(find "$1" -name '*.c' | sort)" + ;; + esac + shift +done + +if [ -z "$tests_list" ]; then + tests_list=`find . -name '*.c' | sed -e 's#^\./\(.*\)#\1#' | sort` +fi + +do_test_suite exit $failed diff --git a/usr/src/tools/smatch/src/validation/testsuite-selfcheck1.c b/usr/src/tools/smatch/src/validation/testsuite-selfcheck1.c deleted file mode 100644 index d927f9961e..0000000000 --- a/usr/src/tools/smatch/src/validation/testsuite-selfcheck1.c +++ /dev/null @@ -1,10 +0,0 @@ -good - -/* - * check-name: selfcheck1 - * check-command: sparse -E $file - * check-output-ignore - * - * check-output-contains: good - * check-output-excludes: evil - */ diff --git a/usr/src/tools/smatch/src/validation/testsuite-selfcheck2.c b/usr/src/tools/smatch/src/validation/testsuite-selfcheck2.c deleted file mode 100644 index 5309e32f37..0000000000 --- a/usr/src/tools/smatch/src/validation/testsuite-selfcheck2.c +++ /dev/null @@ -1,10 +0,0 @@ -evil - -/* - * check-name: selfcheck2 - * check-command: sparse -E $file - * check-output-ignore - * check-known-to-fail - * - * check-output-contains: good - */ diff --git a/usr/src/tools/smatch/src/validation/testsuite-selfcheck3.c b/usr/src/tools/smatch/src/validation/testsuite-selfcheck3.c deleted file mode 100644 index 6d834e68d6..0000000000 --- a/usr/src/tools/smatch/src/validation/testsuite-selfcheck3.c +++ /dev/null @@ -1,10 +0,0 @@ -evil - -/* - * check-name: selfcheck3 - * check-command: sparse -E $file - * check-output-ignore - * check-known-to-fail - * - * check-output-excludes: evil - */ diff --git a/usr/src/tools/smatch/src/validation/type-compare.c b/usr/src/tools/smatch/src/validation/type-compare.c new file mode 100644 index 0000000000..1adcd7040f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/type-compare.c @@ -0,0 +1,76 @@ +#define __user __attribute__((address_space(1))) +#define __safe __attribute__((safe)) +#define __nocast __attribute__((nocast)) +#define __bitwise __attribute__((bitwise)) +#define __noderef __attribute__((noderef)) + +int test(void) +{ + if ([int] != [int]) return 1; + if (!([int] == [int])) return 1; + + if ([int] == [long]) return 1; + if (!([int] != [long])) return 1; + + if ([int] == [unsigned int]) return 1; + if (!([int] != [unsigned int])) return 1; + + if ([int] != [int]) return 1; + if ([typeof(int)] != [int]) return 1; + if ([int] != [typeof(int)]) return 1; + if ([typeof(int)] != [typeof(int)]) return 1; + + if ([char] > [short]) return 1; + if ([short] < [char]) return 1; + if (!([char] <= [short])) return 1; + if (!([short] >= [char])) return 1; + + if ([short] > [int]) return 1; + if ([int] < [short]) return 1; + if (!([short] <= [int])) return 1; + if (!([int] >= [short])) return 1; + + if ([int] > [long]) return 1; + if ([long] < [int]) return 1; + if (!([int] <= [long])) return 1; + if (!([long] >= [int])) return 1; + + if ([long] > [long long]) return 1; + if ([long long] < [long]) return 1; + if (!([long] <= [long long])) return 1; + if (!([long long] >= [long])) return 1; + + if ([int *] != [int *]) return 1; + if ([int *] == [void *]) return 1; + + // qualifiers are ignored + if ([int] != [const int]) return 1; + if ([int] != [volatile int]) return 1; + + // but others modifiers are significant + if ([int] == [int __nocast]) return 1; + if ([int] == [int __bitwise]) return 1; + + // + if ([int *] == [const int *]) return 1; + if ([int *] == [volatile int *]) return 1; + if ([int *] == [int __user *]) return 1; + if ([int *] == [int __safe *]) return 1; + if ([int *] == [int __nocast *]) return 1; + if ([int *] == [int __bitwise *]) return 1; + if ([int *] == [int __noderef *]) return 1; + + return 0; +} + +/* + * check-name: type-as-first-class comparison + * check-description: This test the sparse extension making + * types first class citizens which can be compared + * for equality (or size for <, >, <=, >=). + * See expand.c:compare_types(). + * check-command: test-linearize -Wno-decl $file + * check-output-ignore + * + * check-output-contains: ret\\..*\\$0 + */ diff --git a/usr/src/tools/smatch/src/validation/typedef-redef-c89.c b/usr/src/tools/smatch/src/validation/typedef-redef-c89.c new file mode 100644 index 0000000000..6d4dc28c13 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/typedef-redef-c89.c @@ -0,0 +1,13 @@ +typedef int int_t; +typedef int int_t; + +/* + * check-name: typedef-redef-c89 + * check-command: sparse -std=c89 --pedantic $file + * check-known-to-fail + * + * check-error-start +typedef-redef-c89.c:2:13: warning: redefinition of typedef 'int_t' +typedef-redef-c89.c:1:13: info: originally defined here + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/typedef-redef.c b/usr/src/tools/smatch/src/validation/typedef-redef.c new file mode 100644 index 0000000000..3a60a77316 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/typedef-redef.c @@ -0,0 +1,13 @@ +typedef int ok_t; +typedef int ok_t; + +typedef int ko_t; +typedef long ko_t; + +/* + * check-name: typedef-redef + * + * check-error-start +typedef-redef.c:5:14: error: symbol 'ko_t' redeclared with different type (originally declared at typedef-redef.c:4) - different type sizes + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/typedef_shadow.c b/usr/src/tools/smatch/src/validation/typedef_shadow.c index e52de80f27..e8e4c229b1 100644 --- a/usr/src/tools/smatch/src/validation/typedef_shadow.c +++ b/usr/src/tools/smatch/src/validation/typedef_shadow.c @@ -5,9 +5,9 @@ static void f(int T) } /* * check-name: typedef shadowing - * check-error-start: + * check-error-start typedef_shadow.c:4:16: warning: 'T' has implicit type typedef_shadow.c:4:18: error: Expected ; at end of declaration typedef_shadow.c:4:18: error: got a - * check-error-end: + * check-error-end */ diff --git a/usr/src/tools/smatch/src/validation/typediff-arraysize.c b/usr/src/tools/smatch/src/validation/typediff-arraysize.c new file mode 100644 index 0000000000..dd7a2ca5ad --- /dev/null +++ b/usr/src/tools/smatch/src/validation/typediff-arraysize.c @@ -0,0 +1,12 @@ +extern int ok0[]; int ok0[1]; // OK +extern int ok1[1]; int ok1[]; // OK but size should be 1 +extern int ko1[1]; int ko1[2]; // KO + +/* + * check-name: typediff-arraysize + * check-known-to-fail + * + * check-error-start +typediff-arraysize.c:3:29: error: symbol 'ko1' redeclared with different type (originally declared at typediff-arraysize.c:3) - different array sizes + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/typediff-enum.c b/usr/src/tools/smatch/src/validation/typediff-enum.c new file mode 100644 index 0000000000..c5f2dc0a3f --- /dev/null +++ b/usr/src/tools/smatch/src/validation/typediff-enum.c @@ -0,0 +1,34 @@ +enum num { ZERO, ONE, MANY, }; +typedef enum num num; + +extern int v; +num v = 0; + +extern num w; +int w = 0; + +int foo(void); +num foo(void) { return ZERO; } + +num bar(void); +int bar(void) { return ZERO; } + +void baz(int a); +void baz(num a) { } + +void qux(num a); +void qux(int a) { } + +/* + * check-name: typediff-enum + * check-known-to-fail + * + * check-error-start +typediff-enum.c:5:5: error: symbol 'v' redeclared with different type (originally declared at typediff-enum.c:4) - different types +typediff-enum.c:8:5: error: symbol 'w' redeclared with different type (originally declared at typediff-enum.c:7) - different types +typediff-enum.c:11:5: error: symbol 'foo' redeclared with different type (originally declared at typediff-enum.c:10) - different types +typediff-enum.c:14:5: error: symbol 'bar' redeclared with different type (originally declared at typediff-enum.c:13) - different types +typediff-enum.c:17:6: error: symbol 'baz' redeclared with different type (originally declared at typediff-enum.c:16) - incompatible argument 1 (different types) +typediff-enum.c:20:6: error: symbol 'qux' redeclared with different type (originally declared at typediff-enum.c:19) - incompatible argument 1 (different types) + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/typeof-bad.c b/usr/src/tools/smatch/src/validation/typeof-bad.c new file mode 100644 index 0000000000..a9366bad1d --- /dev/null +++ b/usr/src/tools/smatch/src/validation/typeof-bad.c @@ -0,0 +1,17 @@ +static typeof(undef) a; + +static int foo(void) +{ + return a; +} + +/* + * check-name: typeof-bad + * + * check-error-start +typeof-bad.c:1:15: error: undefined identifier 'undef' +typeof-bad.c:5:16: warning: incorrect type in return expression (different base types) +typeof-bad.c:5:16: expected int +typeof-bad.c:5:16: got bad type static [toplevel] a + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/typeof-mods.c b/usr/src/tools/smatch/src/validation/typeof-mods.c index 9822e96f6c..aa880f3730 100644 --- a/usr/src/tools/smatch/src/validation/typeof-mods.c +++ b/usr/src/tools/smatch/src/validation/typeof-mods.c @@ -43,6 +43,34 @@ static void test_volatile(void) obj = *ptr; } +static void test_restrict(void) +{ + int *restrict obj, *restrict *ptr; + typeof(obj) var = obj; + typeof(ptr) ptr2 = ptr; + typeof(*ptr) var2 = obj; + typeof(*ptr) *ptr3 = ptr; + typeof(obj) *ptr4 = ptr; + obj = obj; + ptr = ptr; + ptr = &obj; + obj = *ptr; +} + +static void test_atomic(void) +{ + int _Atomic obj, *ptr; + typeof(obj) var = obj; + typeof(ptr) ptr2 = ptr; + typeof(*ptr) var2 = obj; + typeof(*ptr) *ptr3 = ptr; + typeof(obj) *ptr4 = ptr; + obj = obj; + ptr = ptr; + ptr = &obj; + obj = *ptr; +} + static void test_bitwise(void) { typedef int __bitwise type_t; diff --git a/usr/src/tools/smatch/src/validation/var-undef-partial.c b/usr/src/tools/smatch/src/validation/var-undef-partial.c new file mode 100644 index 0000000000..f1a07bea71 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/var-undef-partial.c @@ -0,0 +1,20 @@ +int foo(int a, int b) +{ + int var = 0; + int r; + + if (a) + var = 1; + if (b) + r = var; + + return r; // undef if !b +} + +/* + * check-name: variable partially undefined + * check-description: trigger a bug in symbol/memop simplification + * check-description: sparse-llvm is used here as semantic checker of sparse's IR + * check-command: sparse-llvm -Wno-decl $file + * check-output-ignore + */ diff --git a/usr/src/tools/smatch/src/validation/vla-sizeof-ice.c b/usr/src/tools/smatch/src/validation/vla-sizeof-ice.c new file mode 100644 index 0000000000..472da6a419 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/vla-sizeof-ice.c @@ -0,0 +1,19 @@ +// credit goes to Martin Uecker for the awesome ICE_P macro + +#define ICE_P(x) \ + (__builtin_types_compatible_p(typeof(0?((void*)((long)(x)*0l)):(int*)1),int*)) + +#define T(x) __builtin_choose_expr(ICE_P(x), 1, 0) +#define TEST(x, r) _Static_assert(T(x) == r, #x " => " #r) + +static void test(int n) +{ + char foo[n++]; + + TEST(sizeof(foo), 0); +} + +/* + * check-name: vla-sizeof-ice + * check-command: sparse -Wno-vla $file + */ diff --git a/usr/src/tools/smatch/src/validation/vla-sizeof.c b/usr/src/tools/smatch/src/validation/vla-sizeof.c new file mode 100644 index 0000000000..43079992c0 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/vla-sizeof.c @@ -0,0 +1,37 @@ +unsigned long vla_sizeof0(int size) +{ + int a[size]; + return sizeof(a); +} + +unsigned long vla_sizeof1(int size) +{ + struct s { + int a[size]; + }; + return sizeof(struct s); +} + +unsigned long vla_sizeof2(int size) +{ + struct s { + int a[size]; + } *p; + return sizeof(*p); +} + +void* vla_inc(int size, void *base) +{ + struct s { + int a[size]; + } *p = base; + + ++p; + return p; +} + +/* + * check-name: vla-sizeof.c + * + * check-known-to-fail + */ diff --git a/usr/src/tools/smatch/src/validation/vla-sizeof0.c b/usr/src/tools/smatch/src/validation/vla-sizeof0.c new file mode 100644 index 0000000000..a58fd300f0 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/vla-sizeof0.c @@ -0,0 +1,20 @@ +#define N 2 +#define T int + +static unsigned int foo(int x) +{ + T a[(1,N)]; + + return sizeof(a) == (N * sizeof(T)); +} + +/* + * check-name: vla-sizeof cte,cte + * check-command: test-linearize -Wvla $file + * + * check-output-ignore + * check-output-contains: ret\\.32 *\\$1 + * + * check-error-start + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/vla-sizeof1.c b/usr/src/tools/smatch/src/validation/vla-sizeof1.c new file mode 100644 index 0000000000..ed7f5d4ee8 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/vla-sizeof1.c @@ -0,0 +1,21 @@ +#define N 2 +#define T int + +static unsigned int foo(int x) +{ + T a[(x,N)]; + + return sizeof(a) == (N * sizeof(T)); +} + +/* + * check-name: vla-sizeof var,cte + * check-command: test-linearize -Wvla $file + * + * check-output-ignore + * check-output-contains: ret\\.32 *\\$1 + * + * check-error-start +vla-sizeof1.c:6:15: warning: Variable length array is used. + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/vla-sizeof2.c b/usr/src/tools/smatch/src/validation/vla-sizeof2.c new file mode 100644 index 0000000000..57927d1693 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/vla-sizeof2.c @@ -0,0 +1,21 @@ +#define N 2 +#define T int + +static unsigned long foo(int x) +{ + T a[x]; + + return sizeof(a) == (x * sizeof(T)); +} + +/* + * check-name: vla-sizeof var + * check-command: test-linearize -Wvla $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$1 + * + * check-error-start +vla-sizeof2.c:6:13: warning: Variable length array is used. + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/vla-sizeof3.c b/usr/src/tools/smatch/src/validation/vla-sizeof3.c new file mode 100644 index 0000000000..21edb00780 --- /dev/null +++ b/usr/src/tools/smatch/src/validation/vla-sizeof3.c @@ -0,0 +1,21 @@ +#define N 2UL +#define T int + +static unsigned long foo(int x) +{ + T a[x][N]; + + return sizeof(a) == (N * x * sizeof(T)); +} + +/* + * check-name: vla-sizeof var X cte + * check-command: test-linearize -Wvla $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$1 + * + * check-error-start +vla-sizeof3.c:6:13: warning: Variable length array is used. + * check-error-end + */ diff --git a/usr/src/tools/smatch/src/validation/vla-sizeof4.c b/usr/src/tools/smatch/src/validation/vla-sizeof4.c new file mode 100644 index 0000000000..e7478613de --- /dev/null +++ b/usr/src/tools/smatch/src/validation/vla-sizeof4.c @@ -0,0 +1,22 @@ +#define N 2 +#define T int + +static unsigned long foo(int x, int y) +{ + T a[x][y]; + + return sizeof(a) == (x * (y * sizeof(T))); +} + +/* + * check-name: vla-sizeof var X var + * check-command: test-linearize -Wvla $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$1 + * + * check-error-start +vla-sizeof4.c:6:16: warning: Variable length array is used. +vla-sizeof4.c:6:13: warning: Variable length array is used. + * check-error-end + */ diff --git a/usr/src/uts/common/io/mac/mac.c b/usr/src/uts/common/io/mac/mac.c index 05a382be2f..8709d07030 100644 --- a/usr/src/uts/common/io/mac/mac.c +++ b/usr/src/uts/common/io/mac/mac.c @@ -4853,7 +4853,7 @@ mac_group_mov_ring(mac_impl_t *mip, mac_group_t *d_group, mac_ring_t *ring) ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); ASSERT(d_group != NULL); - ASSERT(s_group->mrg_mh == d_group->mrg_mh); + ASSERT(s_group == NULL || s_group->mrg_mh == d_group->mrg_mh); if (s_group == d_group) return (0); diff --git a/usr/src/uts/intel/mac/Makefile b/usr/src/uts/intel/mac/Makefile index 6821a77c5f..667c13744a 100644 --- a/usr/src/uts/intel/mac/Makefile +++ b/usr/src/uts/intel/mac/Makefile @@ -22,7 +22,7 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright (c) 2018, Joyent, Inc. +# Copyright 2019 Joyent, Inc. # # Path to the base of the uts directory tree (usually /usr/src/uts). @@ -67,11 +67,8 @@ CERRWARN += -_gcc=-Wno-unused-variable # needs work SMOFF += all_func_returns -$(OBJS_DIR)/mac.o := SMOFF += deref_check $(OBJS_DIR)/mac_util.o := SMOFF += signed -# false positive -$(OBJS_DIR)/mac_sched.o := SMOFF += assign_vs_compare # # Default build targets. # diff --git a/usr/src/uts/intel/procfs/Makefile b/usr/src/uts/intel/procfs/Makefile index ce2c05dcbe..1db5848438 100644 --- a/usr/src/uts/intel/procfs/Makefile +++ b/usr/src/uts/intel/procfs/Makefile @@ -24,7 +24,7 @@ # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright (c) 2018, Joyent, Inc. +# Copyright 2019 Joyent, Inc. # # This makefile drives the production of the procfs file system @@ -83,9 +83,6 @@ $(OBJS_DIR)/prsubr.o := SMOFF += all_func_returns $(OBJS_DIR)/prcontrol.o := SMOFF += all_func_returns $(OBJS_DIR)/prioctl.o := SMOFF += signed -# false positives -$(OBJS_DIR)/prvnops.o := SMOFF += strcpy_overflow - # # Default build targets. # diff --git a/usr/src/uts/intel/sol_ofs/Makefile b/usr/src/uts/intel/sol_ofs/Makefile index ab17e465ad..c6a548a28e 100644 --- a/usr/src/uts/intel/sol_ofs/Makefile +++ b/usr/src/uts/intel/sol_ofs/Makefile @@ -21,7 +21,8 @@ # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # -# Copyright (c) 2018, Joyent, Inc. +# Copyright 2019 Joyent, Inc. +# # @@ -68,6 +69,9 @@ CERRWARN += -_gcc=-Wno-unused-variable # needs work $(OBJS_DIR)/sol_cma.o := SMOFF += deref_check +# false positive +SMOFF += signed + # # Default build targets. # diff --git a/usr/src/uts/intel/sol_uverbs/Makefile b/usr/src/uts/intel/sol_uverbs/Makefile index 0a9fdbada8..89eede3923 100644 --- a/usr/src/uts/intel/sol_uverbs/Makefile +++ b/usr/src/uts/intel/sol_uverbs/Makefile @@ -21,7 +21,7 @@ # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # -# Copyright (c) 2018, Joyent, Inc. +# Copyright 2019 Joyent, Inc. # # Path to the base of the uts directory tree (usually /usr/src/uts). @@ -67,6 +67,9 @@ CERRWARN += -_gcc=-Wno-unused-label # really broken SMOFF += logical_instead_of_bitwise,or_vs_and +# false positive +SMOFF += signed + # # Default build targets. # diff --git a/usr/src/uts/intel/zfs/Makefile b/usr/src/uts/intel/zfs/Makefile index 698d3bd8fa..8847b2445f 100644 --- a/usr/src/uts/intel/zfs/Makefile +++ b/usr/src/uts/intel/zfs/Makefile @@ -103,9 +103,8 @@ $(OBJS_DIR)/zfs_vnops.o := SMOFF += signed # needs work $(OBJS_DIR)/zvol.o := SMOFF += deref_check,signed -# false positives +# false positive $(OBJS_DIR)/zfs_ctldir.o := SMOFF += strcpy_overflow -$(OBJS_DIR)/zfs_ioctl.o := SMOFF += strcpy_overflow # # Default build targets. |