summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/auditreduce/Makefile6
-rw-r--r--usr/src/cmd/auditreduce/main.c22
-rw-r--r--usr/src/lib/udapl/libdat/Makefile.com12
-rw-r--r--usr/src/tools/smatch/Makefile224
-rw-r--r--usr/src/tools/smatch/src/Documentation/.gitignore2
-rw-r--r--usr/src/tools/smatch/src/Documentation/IR.rst427
-rw-r--r--usr/src/tools/smatch/src/Documentation/Makefile26
-rw-r--r--usr/src/tools/smatch/src/Documentation/TODO.md98
-rw-r--r--usr/src/tools/smatch/src/Documentation/api.rst27
-rw-r--r--usr/src/tools/smatch/src/Documentation/arm64-detecting-tagged-addresses.txt207
-rw-r--r--usr/src/tools/smatch/src/Documentation/conf.py178
-rw-r--r--usr/src/tools/smatch/src/Documentation/dev-options.rst58
-rw-r--r--usr/src/tools/smatch/src/Documentation/doc-guide.rst155
-rw-r--r--usr/src/tools/smatch/src/Documentation/index.rst38
-rw-r--r--usr/src/tools/smatch/src/Documentation/logo.svg94
-rw-r--r--usr/src/tools/smatch/src/Documentation/nocast-vs-bitwise.md41
-rw-r--r--usr/src/tools/smatch/src/Documentation/project-ideas.md52
-rw-r--r--usr/src/tools/smatch/src/Documentation/smatch.txt4
-rw-r--r--usr/src/tools/smatch/src/Documentation/sparse.txt45
-rwxr-xr-xusr/src/tools/smatch/src/Documentation/sphinx/cdoc.py315
-rwxr-xr-xusr/src/tools/smatch/src/Documentation/sphinx/ir.py75
-rw-r--r--usr/src/tools/smatch/src/Documentation/test-suite136
-rw-r--r--usr/src/tools/smatch/src/Documentation/test-suite.rst169
-rw-r--r--usr/src/tools/smatch/src/Makefile534
-rw-r--r--usr/src/tools/smatch/src/README73
-rw-r--r--usr/src/tools/smatch/src/allocate.c2
-rw-r--r--usr/src/tools/smatch/src/allocate.h2
-rw-r--r--usr/src/tools/smatch/src/bits.h61
-rw-r--r--usr/src/tools/smatch/src/builtin.c355
-rw-r--r--usr/src/tools/smatch/src/c2xml.c4
-rwxr-xr-xusr/src/tools/smatch/src/cgcc129
-rw-r--r--usr/src/tools/smatch/src/char.c4
-rw-r--r--usr/src/tools/smatch/src/check_access_ok_math.c19
-rw-r--r--usr/src/tools/smatch/src/check_arm64_tagged.c255
-rw-r--r--usr/src/tools/smatch/src/check_check_deref.c5
-rw-r--r--usr/src/tools/smatch/src/check_continue_vs_break.c2
-rw-r--r--usr/src/tools/smatch/src/check_debug.c8
-rw-r--r--usr/src/tools/smatch/src/check_deref.c2
-rw-r--r--usr/src/tools/smatch/src/check_deref_check.c5
-rw-r--r--usr/src/tools/smatch/src/check_dereferences_param.c2
-rw-r--r--usr/src/tools/smatch/src/check_double_checking.c7
-rw-r--r--usr/src/tools/smatch/src/check_free.c4
-rw-r--r--usr/src/tools/smatch/src/check_free_strict.c22
-rw-r--r--usr/src/tools/smatch/src/check_get_user_overflow.c9
-rw-r--r--usr/src/tools/smatch/src/check_kernel.c8
-rw-r--r--usr/src/tools/smatch/src/check_list.h5
-rw-r--r--usr/src/tools/smatch/src/check_locking.c4
-rw-r--r--usr/src/tools/smatch/src/check_memory.c466
-rw-r--r--usr/src/tools/smatch/src/check_memset.c29
-rw-r--r--usr/src/tools/smatch/src/check_nospec.c2
-rw-r--r--usr/src/tools/smatch/src/check_readl_infinite_loops.c2
-rw-r--r--usr/src/tools/smatch/src/check_rosenberg.c80
-rw-r--r--usr/src/tools/smatch/src/check_testing_index_after_use.c2
-rw-r--r--usr/src/tools/smatch/src/check_uninitialized.c6
-rw-r--r--usr/src/tools/smatch/src/check_unwind.c2
-rw-r--r--usr/src/tools/smatch/src/compat.h2
-rw-r--r--usr/src/tools/smatch/src/compile-i386.c38
-rw-r--r--usr/src/tools/smatch/src/compile.c4
-rw-r--r--usr/src/tools/smatch/src/cse.c148
-rw-r--r--usr/src/tools/smatch/src/cse.h11
-rw-r--r--usr/src/tools/smatch/src/ctags.c6
-rw-r--r--usr/src/tools/smatch/src/dominate.c153
-rw-r--r--usr/src/tools/smatch/src/dominate.h13
-rw-r--r--usr/src/tools/smatch/src/evaluate.c427
-rw-r--r--usr/src/tools/smatch/src/evaluate.h28
-rw-r--r--usr/src/tools/smatch/src/example.c60
-rw-r--r--usr/src/tools/smatch/src/expand.c96
-rw-r--r--usr/src/tools/smatch/src/expression.c6
-rw-r--r--usr/src/tools/smatch/src/expression.h28
-rw-r--r--usr/src/tools/smatch/src/flow.c415
-rw-r--r--usr/src/tools/smatch/src/flow.h25
-rw-r--r--usr/src/tools/smatch/src/flowgraph.c219
-rw-r--r--usr/src/tools/smatch/src/flowgraph.h13
-rw-r--r--usr/src/tools/smatch/src/gcc-attr-list.h11
-rw-r--r--usr/src/tools/smatch/src/gdbhelpers12
-rw-r--r--usr/src/tools/smatch/src/graph.c20
-rw-r--r--usr/src/tools/smatch/src/ident-list.h9
-rw-r--r--usr/src/tools/smatch/src/inline.c50
-rw-r--r--usr/src/tools/smatch/src/ir.c206
-rw-r--r--usr/src/tools/smatch/src/ir.h8
-rw-r--r--usr/src/tools/smatch/src/lib.c1032
-rw-r--r--usr/src/tools/smatch/src/lib.h57
-rw-r--r--usr/src/tools/smatch/src/linearize.c998
-rw-r--r--usr/src/tools/smatch/src/linearize.h204
-rw-r--r--usr/src/tools/smatch/src/liveness.c43
-rw-r--r--usr/src/tools/smatch/src/liveness.h11
-rw-r--r--usr/src/tools/smatch/src/machine.h83
-rw-r--r--usr/src/tools/smatch/src/macro_table.c41
-rw-r--r--usr/src/tools/smatch/src/memops.c44
-rw-r--r--usr/src/tools/smatch/src/obfuscate.c4
-rw-r--r--usr/src/tools/smatch/src/opcode.c38
-rw-r--r--usr/src/tools/smatch/src/opcode.def114
-rw-r--r--usr/src/tools/smatch/src/opcode.h33
-rw-r--r--usr/src/tools/smatch/src/optimize.c125
-rw-r--r--usr/src/tools/smatch/src/optimize.h9
-rw-r--r--usr/src/tools/smatch/src/parse.c467
-rw-r--r--usr/src/tools/smatch/src/parse.h1
-rw-r--r--usr/src/tools/smatch/src/pre-process.c323
-rw-r--r--usr/src/tools/smatch/src/ptrlist.c298
-rw-r--r--usr/src/tools/smatch/src/ptrlist.h436
-rw-r--r--usr/src/tools/smatch/src/ptrmap.c109
-rw-r--r--usr/src/tools/smatch/src/ptrmap.h28
-rw-r--r--usr/src/tools/smatch/src/scope.h2
-rw-r--r--usr/src/tools/smatch/src/show-parse.c133
-rw-r--r--usr/src/tools/smatch/src/simplify.c1316
-rw-r--r--usr/src/tools/smatch/src/smatch.h40
-rw-r--r--usr/src/tools/smatch/src/smatch_array_values.c27
-rw-r--r--usr/src/tools/smatch/src/smatch_assigned_expr.c2
-rw-r--r--usr/src/tools/smatch/src/smatch_auto_copy.c79
-rw-r--r--usr/src/tools/smatch/src/smatch_bits.c2
-rw-r--r--usr/src/tools/smatch/src/smatch_buf_size.c54
-rw-r--r--usr/src/tools/smatch/src/smatch_common_functions.c13
-rw-r--r--usr/src/tools/smatch/src/smatch_comparison.c483
-rw-r--r--usr/src/tools/smatch/src/smatch_conditions.c4
-rwxr-xr-xusr/src/tools/smatch/src/smatch_data/db/clear_user_data.sh2
-rwxr-xr-xusr/src/tools/smatch/src/smatch_data/db/copy_function_pointers.pl55
-rwxr-xr-xusr/src/tools/smatch/src/smatch_data/db/create_db.sh1
-rwxr-xr-xusr/src/tools/smatch/src/smatch_data/db/fill_db_sql.pl2
-rwxr-xr-xusr/src/tools/smatch/src/smatch_data/db/fixup_kernel.sh6
-rw-r--r--usr/src/tools/smatch/src/smatch_data/db/kernel.return_fixes4
-rwxr-xr-xusr/src/tools/smatch/src/smatch_data/db/smdb.py121
-rwxr-xr-xusr/src/tools/smatch/src/smatch_data/db/vim_smdb16
-rw-r--r--usr/src/tools/smatch/src/smatch_data/kernel.bit_shifters.remove4
-rw-r--r--usr/src/tools/smatch/src/smatch_data/kernel.check_string_condition.ignore1
-rw-r--r--usr/src/tools/smatch/src/smatch_data/kernel.ignore_casted_params1
-rw-r--r--usr/src/tools/smatch/src/smatch_data/kernel.ignore_side_effects3
-rw-r--r--usr/src/tools/smatch/src/smatch_data/kernel.ignore_uninitialized_param3
-rw-r--r--usr/src/tools/smatch/src/smatch_data/kernel.unreachable.ignore3
-rw-r--r--usr/src/tools/smatch/src/smatch_db.c106
-rw-r--r--usr/src/tools/smatch/src/smatch_estate.c26
-rw-r--r--usr/src/tools/smatch/src/smatch_expressions.c76
-rw-r--r--usr/src/tools/smatch/src/smatch_extra.c119
-rw-r--r--usr/src/tools/smatch/src/smatch_extra.h6
-rw-r--r--usr/src/tools/smatch/src/smatch_flow.c31
-rw-r--r--usr/src/tools/smatch/src/smatch_function_hooks.c36
-rw-r--r--usr/src/tools/smatch/src/smatch_function_ptrs.c55
-rw-r--r--usr/src/tools/smatch/src/smatch_helper.c51
-rw-r--r--usr/src/tools/smatch/src/smatch_hooks.c220
-rw-r--r--usr/src/tools/smatch/src/smatch_implied.c42
-rw-r--r--usr/src/tools/smatch/src/smatch_integer_overflow.c2
-rw-r--r--usr/src/tools/smatch/src/smatch_kernel_user_data.c101
-rw-r--r--usr/src/tools/smatch/src/smatch_local_values.c244
-rw-r--r--usr/src/tools/smatch/src/smatch_math.c36
-rw-r--r--usr/src/tools/smatch/src/smatch_mem_tracker.c24
-rw-r--r--usr/src/tools/smatch/src/smatch_modification_hooks.c22
-rw-r--r--usr/src/tools/smatch/src/smatch_mtag.c2
-rw-r--r--usr/src/tools/smatch/src/smatch_mtag_data.c53
-rw-r--r--usr/src/tools/smatch/src/smatch_nul_terminator.c25
-rw-r--r--usr/src/tools/smatch/src/smatch_param_filter.c17
-rw-r--r--usr/src/tools/smatch/src/smatch_param_limit.c11
-rw-r--r--usr/src/tools/smatch/src/smatch_param_set.c92
-rw-r--r--usr/src/tools/smatch/src/smatch_param_to_mtag_data.c14
-rw-r--r--usr/src/tools/smatch/src/smatch_param_used.c20
-rw-r--r--usr/src/tools/smatch/src/smatch_parse_call_math.c43
-rw-r--r--usr/src/tools/smatch/src/smatch_ranges.c121
-rw-r--r--usr/src/tools/smatch/src/smatch_real_absolute.c33
-rw-r--r--usr/src/tools/smatch/src/smatch_returns.c13
-rwxr-xr-xusr/src/tools/smatch/src/smatch_scripts/build_generic_data.sh9
-rwxr-xr-xusr/src/tools/smatch/src/smatch_scripts/gen_dma_funcs.sh1
-rwxr-xr-xusr/src/tools/smatch/src/smatch_scripts/gen_rosenberg_funcs.sh4
-rwxr-xr-xusr/src/tools/smatch/src/smatch_scripts/kchecker9
-rwxr-xr-xusr/src/tools/smatch/src/smatch_scripts/summarize_errs.sh2
-rwxr-xr-xusr/src/tools/smatch/src/smatch_scripts/test_generic.sh11
-rwxr-xr-xusr/src/tools/smatch/src/smatch_scripts/test_kernel.sh11
-rw-r--r--usr/src/tools/smatch/src/smatch_slist.c48
-rw-r--r--usr/src/tools/smatch/src/smatch_states.c71
-rw-r--r--usr/src/tools/smatch/src/smatch_struct_assignment.c5
-rw-r--r--usr/src/tools/smatch/src/smatch_type.c20
-rw-r--r--usr/src/tools/smatch/src/smatch_type_val.c109
-rw-r--r--usr/src/tools/smatch/src/smatch_untracked_param.c29
-rw-r--r--usr/src/tools/smatch/src/sort.c6
-rwxr-xr-xusr/src/tools/smatch/src/sparse-llvm-dis15
-rw-r--r--usr/src/tools/smatch/src/sparse-llvm.c813
-rw-r--r--usr/src/tools/smatch/src/sparse.1106
-rw-r--r--usr/src/tools/smatch/src/sparse.c50
-rw-r--r--usr/src/tools/smatch/src/sparse.pc.in9
-rwxr-xr-xusr/src/tools/smatch/src/sparsec19
-rwxr-xr-xusr/src/tools/smatch/src/sparsei20
-rw-r--r--usr/src/tools/smatch/src/ssa.c402
-rw-r--r--usr/src/tools/smatch/src/ssa.h8
-rw-r--r--usr/src/tools/smatch/src/sset.c28
-rw-r--r--usr/src/tools/smatch/src/sset.h56
-rw-r--r--usr/src/tools/smatch/src/symbol.c69
-rw-r--r--usr/src/tools/smatch/src/symbol.h182
-rw-r--r--usr/src/tools/smatch/src/target.c123
-rw-r--r--usr/src/tools/smatch/src/target.h10
-rw-r--r--usr/src/tools/smatch/src/test-dissect.c4
-rw-r--r--usr/src/tools/smatch/src/test-inspect.c4
-rw-r--r--usr/src/tools/smatch/src/test-lexing.c4
-rw-r--r--usr/src/tools/smatch/src/test-linearize.c6
-rw-r--r--usr/src/tools/smatch/src/test-parsing.c4
-rw-r--r--usr/src/tools/smatch/src/test-unssa.c8
-rw-r--r--usr/src/tools/smatch/src/token.h3
-rw-r--r--usr/src/tools/smatch/src/tokenize.c50
-rw-r--r--usr/src/tools/smatch/src/unssa.c7
-rw-r--r--usr/src/tools/smatch/src/utils.c47
-rw-r--r--usr/src/tools/smatch/src/utils.h43
-rw-r--r--usr/src/tools/smatch/src/validation/Waddress-array.c25
-rw-r--r--usr/src/tools/smatch/src/validation/Waddress-function.c17
-rw-r--r--usr/src/tools/smatch/src/validation/Waddress-space-all-attr.c64
-rw-r--r--usr/src/tools/smatch/src/validation/Waddress-space-from.c63
-rw-r--r--usr/src/tools/smatch/src/validation/Waddress-space-strict.c36
-rw-r--r--usr/src/tools/smatch/src/validation/Waddress-weak.c27
-rw-r--r--usr/src/tools/smatch/src/validation/Waddress.c114
-rw-r--r--usr/src/tools/smatch/src/validation/Wcast-to-as.c36
-rw-r--r--usr/src/tools/smatch/src/validation/Wexternal-function-has-definition.c12
-rw-r--r--usr/src/tools/smatch/src/validation/Wunknown-attribute-def.c4
-rw-r--r--usr/src/tools/smatch/src/validation/Wunknown-attribute-yes.c2
-rw-r--r--usr/src/tools/smatch/src/validation/abi-integer.c31
-rw-r--r--usr/src/tools/smatch/src/validation/address_space.c2
-rw-r--r--usr/src/tools/smatch/src/validation/array-implicit-size.c26
-rw-r--r--usr/src/tools/smatch/src/validation/as-name.c17
-rw-r--r--usr/src/tools/smatch/src/validation/asm-inline.c52
-rw-r--r--usr/src/tools/smatch/src/validation/attr-context.c40
-rw-r--r--usr/src/tools/smatch/src/validation/backend/arithmetic-ops.c20
-rw-r--r--usr/src/tools/smatch/src/validation/backend/call-variadic.c27
-rw-r--r--usr/src/tools/smatch/src/validation/backend/cast.c7
-rw-r--r--usr/src/tools/smatch/src/validation/backend/compare-with-null.c12
-rw-r--r--usr/src/tools/smatch/src/validation/backend/constant-pointer.c24
-rw-r--r--usr/src/tools/smatch/src/validation/backend/degenerate-ptr.c72
-rw-r--r--usr/src/tools/smatch/src/validation/backend/fn-ref.c32
-rw-r--r--usr/src/tools/smatch/src/validation/backend/function-ptr-xtype.c37
-rw-r--r--usr/src/tools/smatch/src/validation/backend/function-ptr.c148
-rw-r--r--usr/src/tools/smatch/src/validation/backend/label-as-value.c13
-rw-r--r--usr/src/tools/smatch/src/validation/backend/load-global.c21
-rw-r--r--usr/src/tools/smatch/src/validation/backend/pointer-add.c54
-rw-r--r--usr/src/tools/smatch/src/validation/backend/pointer-cmp.c12
-rw-r--r--usr/src/tools/smatch/src/validation/backend/pointer-param.c42
-rw-r--r--usr/src/tools/smatch/src/validation/backend/pointer-sub.c17
-rw-r--r--usr/src/tools/smatch/src/validation/backend/setval.c7
-rw-r--r--usr/src/tools/smatch/src/validation/backend/shift-special.c13
-rw-r--r--usr/src/tools/smatch/src/validation/backend/store-x2.c16
-rw-r--r--usr/src/tools/smatch/src/validation/backend/string-value.c21
-rw-r--r--usr/src/tools/smatch/src/validation/backend/sum.c2
-rw-r--r--usr/src/tools/smatch/src/validation/backend/switch.c248
-rw-r--r--usr/src/tools/smatch/src/validation/backend/symaddr.c70
-rw-r--r--usr/src/tools/smatch/src/validation/backend/type-constant.c23
-rw-r--r--usr/src/tools/smatch/src/validation/bad-return-type.c19
-rw-r--r--usr/src/tools/smatch/src/validation/bad-type-twice0.c13
-rw-r--r--usr/src/tools/smatch/src/validation/bad-type-twice1.c16
-rw-r--r--usr/src/tools/smatch/src/validation/bad-type-twice2.c18
-rw-r--r--usr/src/tools/smatch/src/validation/bitfield-bool-layout.c26
-rw-r--r--usr/src/tools/smatch/src/validation/bitfield-kr.c14
-rw-r--r--usr/src/tools/smatch/src/validation/bitwise-cast-ptr.c33
-rw-r--r--usr/src/tools/smatch/src/validation/bitwise-cast.c6
-rw-r--r--usr/src/tools/smatch/src/validation/bool-cast-explicit.c22
-rw-r--r--usr/src/tools/smatch/src/validation/bool-cast-implicit.c25
-rw-r--r--usr/src/tools/smatch/src/validation/bool-float.c9
-rw-r--r--usr/src/tools/smatch/src/validation/bug-bad-type.c18
-rw-r--r--usr/src/tools/smatch/src/validation/bug-crash16.c11
-rw-r--r--usr/src/tools/smatch/src/validation/bug-expand-union0.c21
-rw-r--r--usr/src/tools/smatch/src/validation/bug-expand-union1.c20
-rw-r--r--usr/src/tools/smatch/src/validation/bug-rshift-ub.c16
-rw-r--r--usr/src/tools/smatch/src/validation/builtin-arith.c52
-rw-r--r--usr/src/tools/smatch/src/validation/builtin-bswap-variable.c4
-rw-r--r--usr/src/tools/smatch/src/validation/builtin-fp-unop.c95
-rw-r--r--usr/src/tools/smatch/src/validation/builtin-overflow.c246
-rw-r--r--usr/src/tools/smatch/src/validation/builtin-prototype.c15
-rw-r--r--usr/src/tools/smatch/src/validation/c11-alignas.c2
-rw-r--r--usr/src/tools/smatch/src/validation/c11-alignof.c2
-rw-r--r--usr/src/tools/smatch/src/validation/c11-atomic.c93
-rw-r--r--usr/src/tools/smatch/src/validation/c11-noreturn.c2
-rw-r--r--usr/src/tools/smatch/src/validation/c11-thread-local.c2
-rw-r--r--usr/src/tools/smatch/src/validation/call-inlined.c55
-rw-r--r--usr/src/tools/smatch/src/validation/call-variadic.c23
-rw-r--r--usr/src/tools/smatch/src/validation/cast-bad-00.c47
-rw-r--r--usr/src/tools/smatch/src/validation/cast-bad-01.c13
-rw-r--r--usr/src/tools/smatch/src/validation/cast-kinds-check.c20
-rw-r--r--usr/src/tools/smatch/src/validation/cast-weirds.c19
-rw-r--r--usr/src/tools/smatch/src/validation/char-signed.c9
-rw-r--r--usr/src/tools/smatch/src/validation/char-unsigned.c11
-rw-r--r--usr/src/tools/smatch/src/validation/check_access-multi.c15
-rw-r--r--usr/src/tools/smatch/src/validation/check_access-store.c21
-rw-r--r--usr/src/tools/smatch/src/validation/check_byte_count-ice.c6
-rw-r--r--usr/src/tools/smatch/src/validation/choose_expr.c8
-rw-r--r--usr/src/tools/smatch/src/validation/compound-assign-type.c5
-rw-r--r--usr/src/tools/smatch/src/validation/compound-sizes.c88
-rw-r--r--usr/src/tools/smatch/src/validation/cond-address-array.c26
-rw-r--r--usr/src/tools/smatch/src/validation/cond-address-function.c18
-rw-r--r--usr/src/tools/smatch/src/validation/cond-address.c2
-rw-r--r--usr/src/tools/smatch/src/validation/cond-err-expand.c10
-rw-r--r--usr/src/tools/smatch/src/validation/conditional-type.c16
-rw-r--r--usr/src/tools/smatch/src/validation/constant-suffix-64.c1
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-addr-of-static-member.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-addr-of-static.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-binop.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-cast.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-compound-literal.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-conditional.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-init.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-labelref.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-offsetof.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-pointer-arith.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-pointer-cast.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-preop.c4
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-shift.c12
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-string.c2
-rw-r--r--usr/src/tools/smatch/src/validation/constexpr-types-compatible-p.c2
-rw-r--r--usr/src/tools/smatch/src/validation/context-stmt.c62
-rw-r--r--usr/src/tools/smatch/src/validation/crash-select.c18
-rw-r--r--usr/src/tools/smatch/src/validation/doc/cdoc.cdoc177
-rw-r--r--usr/src/tools/smatch/src/validation/empty-expr.c26
-rw-r--r--usr/src/tools/smatch/src/validation/enum+mode.c18
-rw-r--r--usr/src/tools/smatch/src/validation/enum-base-type.c29
-rw-r--r--usr/src/tools/smatch/src/validation/enum-bitwise-bad.c20
-rw-r--r--usr/src/tools/smatch/src/validation/enum-bitwise-mixed.c29
-rw-r--r--usr/src/tools/smatch/src/validation/enum-bitwise.c19
-rw-r--r--usr/src/tools/smatch/src/validation/enum-bounds.c25
-rw-r--r--usr/src/tools/smatch/src/validation/enum-init-constness.c9
-rw-r--r--usr/src/tools/smatch/src/validation/enum-invalid.c11
-rw-r--r--usr/src/tools/smatch/src/validation/enum-min-size.c29
-rw-r--r--usr/src/tools/smatch/src/validation/enum-mismatch.c4
-rw-r--r--usr/src/tools/smatch/src/validation/enum-same-type.c14
-rw-r--r--usr/src/tools/smatch/src/validation/enum-sign-gcc.c64
-rw-r--r--usr/src/tools/smatch/src/validation/enum-typecheck.c39
-rw-r--r--usr/src/tools/smatch/src/validation/error-at-eof.c10
-rw-r--r--usr/src/tools/smatch/src/validation/eval-typeof-vla.c26
-rw-r--r--usr/src/tools/smatch/src/validation/expand/bad-shift.c64
-rw-r--r--usr/src/tools/smatch/src/validation/expand/builtin-expect.c100
-rw-r--r--usr/src/tools/smatch/src/validation/expand/builtin_fpclassify.c26
-rw-r--r--usr/src/tools/smatch/src/validation/expand/builtin_huge_val.c39
-rw-r--r--usr/src/tools/smatch/src/validation/expand/builtin_isinf.c20
-rw-r--r--usr/src/tools/smatch/src/validation/expand/builtin_isnan.c20
-rw-r--r--usr/src/tools/smatch/src/validation/expand/builtin_isnormal.c20
-rw-r--r--usr/src/tools/smatch/src/validation/expand/builtin_nan.c23
-rw-r--r--usr/src/tools/smatch/src/validation/expand/function-pointer.c22
-rw-r--r--usr/src/tools/smatch/src/validation/external-function-has-definition.c15
-rw-r--r--usr/src/tools/smatch/src/validation/fdiag-prefix.c11
-rw-r--r--usr/src/tools/smatch/src/validation/fp-ops.c57
-rw-r--r--usr/src/tools/smatch/src/validation/function-pointer-type.c12
-rw-r--r--usr/src/tools/smatch/src/validation/function-redecl2.c31
-rw-r--r--usr/src/tools/smatch/src/validation/goto-reserved.c12
-rw-r--r--usr/src/tools/smatch/src/validation/implicit-KR-arg-type1.c16
-rw-r--r--usr/src/tools/smatch/src/validation/inc-dec-float.c13
-rw-r--r--usr/src/tools/smatch/src/validation/incomplete-struct.c23
-rw-r--r--usr/src/tools/smatch/src/validation/infinite-loop01.c54
-rw-r--r--usr/src/tools/smatch/src/validation/infinite-loop02.c1
-rw-r--r--usr/src/tools/smatch/src/validation/infinite-loop03.c1
-rw-r--r--usr/src/tools/smatch/src/validation/infinite-loop04.c18
-rw-r--r--usr/src/tools/smatch/src/validation/int128.c2
-rw-r--r--usr/src/tools/smatch/src/validation/integer-const-expr.c85
-rw-r--r--usr/src/tools/smatch/src/validation/kill-load.c2
-rw-r--r--usr/src/tools/smatch/src/validation/kill-phi-ttsbb.c2
-rw-r--r--usr/src/tools/smatch/src/validation/kill-store.c2
-rw-r--r--usr/src/tools/smatch/src/validation/kill-switch.c17
-rw-r--r--usr/src/tools/smatch/src/validation/label-redefined.c17
-rw-r--r--usr/src/tools/smatch/src/validation/linear/asm-toplevel.c (renamed from usr/src/tools/smatch/src/validation/asm-toplevel.c)0
-rw-r--r--usr/src/tools/smatch/src/validation/linear/bitfield-expand-deref.c27
-rw-r--r--usr/src/tools/smatch/src/validation/linear/bitfield-inc.c16
-rw-r--r--usr/src/tools/smatch/src/validation/linear/bitfield-init-mask.c2
-rw-r--r--usr/src/tools/smatch/src/validation/linear/bitfield-preinc.c18
-rw-r--r--usr/src/tools/smatch/src/validation/linear/bitfield-size.c183
-rw-r--r--usr/src/tools/smatch/src/validation/linear/bitfield-store.c22
-rw-r--r--usr/src/tools/smatch/src/validation/linear/bool-cast-lp32.c19
-rw-r--r--usr/src/tools/smatch/src/validation/linear/bool-cast-lp64.c19
-rw-r--r--usr/src/tools/smatch/src/validation/linear/bool-cast.c37
-rw-r--r--usr/src/tools/smatch/src/validation/linear/builtin_unreachable.c31
-rw-r--r--usr/src/tools/smatch/src/validation/linear/call-basic.c57
-rw-r--r--usr/src/tools/smatch/src/validation/linear/call-builtin.c17
-rw-r--r--usr/src/tools/smatch/src/validation/linear/call-casted-pointer.c31
-rw-r--r--usr/src/tools/smatch/src/validation/linear/call-complex-pointer.c33
-rw-r--r--usr/src/tools/smatch/src/validation/linear/call-direct.c17
-rw-r--r--usr/src/tools/smatch/src/validation/linear/call-indirect.c15
-rw-r--r--usr/src/tools/smatch/src/validation/linear/call-inline.c18
-rw-r--r--usr/src/tools/smatch/src/validation/linear/cast-constant-to-float.c (renamed from usr/src/tools/smatch/src/validation/cast-constant-to-float.c)6
-rw-r--r--usr/src/tools/smatch/src/validation/linear/cast-constants.c (renamed from usr/src/tools/smatch/src/validation/cast-constants.c)21
-rw-r--r--usr/src/tools/smatch/src/validation/linear/cast-volatile.c15
-rw-r--r--usr/src/tools/smatch/src/validation/linear/compound-literal00.c18
-rw-r--r--usr/src/tools/smatch/src/validation/linear/compound-literal01.c18
-rw-r--r--usr/src/tools/smatch/src/validation/linear/compound-literal02.c19
-rw-r--r--usr/src/tools/smatch/src/validation/linear/degen-array.c32
-rw-r--r--usr/src/tools/smatch/src/validation/linear/degen-function.c52
-rw-r--r--usr/src/tools/smatch/src/validation/linear/degen-log-not.c40
-rw-r--r--usr/src/tools/smatch/src/validation/linear/deref-ptr-ptr.c27
-rw-r--r--usr/src/tools/smatch/src/validation/linear/fp-vs-ptrcast.c (renamed from usr/src/tools/smatch/src/validation/fp-vs-ptrcast.c)0
-rw-r--r--usr/src/tools/smatch/src/validation/linear/fp2i-cast.c31
-rw-r--r--usr/src/tools/smatch/src/validation/linear/logical-phi0.c48
-rw-r--r--usr/src/tools/smatch/src/validation/linear/logical.c260
-rw-r--r--usr/src/tools/smatch/src/validation/linear/missing-return0.c10
-rw-r--r--usr/src/tools/smatch/src/validation/linear/missing-return1.c15
-rw-r--r--usr/src/tools/smatch/src/validation/linear/missing-return2.c11
-rw-r--r--usr/src/tools/smatch/src/validation/linear/missing-return3.c18
-rw-r--r--usr/src/tools/smatch/src/validation/linear/missing-return4.c14
-rw-r--r--usr/src/tools/smatch/src/validation/linear/missing-return5.c23
-rw-r--r--usr/src/tools/smatch/src/validation/linear/non-const-case.c37
-rw-r--r--usr/src/tools/smatch/src/validation/linear/phi-order01.c16
-rw-r--r--usr/src/tools/smatch/src/validation/linear/phi-order02.c16
-rw-r--r--usr/src/tools/smatch/src/validation/linear/phi-order03.c8
-rw-r--r--usr/src/tools/smatch/src/validation/linear/phi-order04.c12
-rw-r--r--usr/src/tools/smatch/src/validation/linear/range-op.c31
-rw-r--r--usr/src/tools/smatch/src/validation/linear/unexamined-base-type.c36
-rw-r--r--usr/src/tools/smatch/src/validation/linear/unreachable-label0.c19
-rw-r--r--usr/src/tools/smatch/src/validation/loop-linearization.c136
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/address-used00.c18
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/alias-distinct.c (renamed from usr/src/tools/smatch/src/validation/alias-distinct.c)0
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/alias-mixed.c (renamed from usr/src/tools/smatch/src/validation/alias-mixed.c)0
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/alias-same.c (renamed from usr/src/tools/smatch/src/validation/alias-same.c)0
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/broken-phi02.c27
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/broken-phi03.c28
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/cond-expr.c14
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/cond-expr5.c21
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/dead-phisrc.c17
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/global-direct-undef.c23
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/global-direct.c23
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/global-loop.c20
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/global-noalias.c21
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/global-pointer.c26
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/if-direct.c19
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/if-pointer.c21
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/init-global-array.c21
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/init-local-array.c28
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/init-local-union0.c18
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/init-local-union1.c32
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/init-local32.c27
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/init-local64.c27
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/load-dead.c18
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/load-deadborn.c9
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/loop00.c16
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/loop01-global.c18
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/loop02-array.c23
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/loop02-global.c22
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/loop02-local.c23
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/loop02-pointer.c23
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/missing-return.c34
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/quadra00.c27
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/quadra01.c27
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/quadra02.c18
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/reload-aliasing.c41
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/short-load.c29
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/store-deadborn.c9
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/stray-phisrc.c24
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/struct.c32
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/undef00.c21
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/undef01.c16
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/unused-var.c23
-rw-r--r--usr/src/tools/smatch/src/validation/mem2reg/volatile-store00.c27
-rw-r--r--usr/src/tools/smatch/src/validation/memops-volatile.c15
-rw-r--r--usr/src/tools/smatch/src/validation/missing-return.c20
-rw-r--r--usr/src/tools/smatch/src/validation/multi-input.c11
-rw-r--r--usr/src/tools/smatch/src/validation/nested-declarator.c4
-rw-r--r--usr/src/tools/smatch/src/validation/nested-declarator2.c4
-rw-r--r--usr/src/tools/smatch/src/validation/nocast.c2
-rw-r--r--usr/src/tools/smatch/src/validation/noderef.c2
-rw-r--r--usr/src/tools/smatch/src/validation/optim/address-used01.c18
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-extend.c27
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-extendx.c24
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-lsr.c15
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-bf0.c24
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-bf1.c18
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-bf2.c27
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-bfs.c23
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-bfu.c21
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-bfx.c18
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-constant0.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-constant1.c14
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-constant2.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-crash.c5
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-lsr0.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-lsr1.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-lsr2.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-lsrx.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-mask.c18
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-mask0.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-mask1.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-mask2.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-mask3s.c25
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-mask3u.c25
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-mask4.c25
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-maskx.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-shl0.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-shl1.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-shl2.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-shlx.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-trunc0.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-trunc1.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-trunc2.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-or-truncx.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/and-trunc.c20
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bitfield-init-zero.c (renamed from usr/src/tools/smatch/src/validation/linear/bitfield-init-zero.c)24
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bitfield-size.c (renamed from usr/src/tools/smatch/src/validation/bitfield-size.c)9
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bitfield-store-load0.c44
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bitfield-store-loads.c23
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bitfield-store-loadu.c21
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bits-not-zero.c30
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bool-context-fp.c93
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bool-context.c2
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bool-eq0.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bool-int-bool.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bool-ne0.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bool-neq0.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bool-sext-test.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bool-simplify.c34
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bool-simplify2.c215
-rw-r--r--usr/src/tools/smatch/src/validation/optim/bool-zext-test.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/call-complex-pointer.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/call-inlined.c30
-rw-r--r--usr/src/tools/smatch/src/validation/optim/canonical-add.c55
-rw-r--r--usr/src/tools/smatch/src/validation/optim/canonical-cmp.c124
-rw-r--r--usr/src/tools/smatch/src/validation/optim/canonical-fcmp.c123
-rw-r--r--usr/src/tools/smatch/src/validation/optim/canonical-mul.c24
-rw-r--r--usr/src/tools/smatch/src/validation/optim/cast-kinds.c (renamed from usr/src/tools/smatch/src/validation/cast-kinds.c)169
-rw-r--r--usr/src/tools/smatch/src/validation/optim/cast-nop.c18
-rw-r--r--usr/src/tools/smatch/src/validation/optim/cse-cmp-next.c15
-rw-r--r--usr/src/tools/smatch/src/validation/optim/cse-fcmp.c19
-rw-r--r--usr/src/tools/smatch/src/validation/optim/cse-setfval.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/cse-size.c17
-rw-r--r--usr/src/tools/smatch/src/validation/optim/dup-cond0.c20
-rw-r--r--usr/src/tools/smatch/src/validation/optim/ext-trunc-greater.c17
-rw-r--r--usr/src/tools/smatch/src/validation/optim/ext-trunc-same.c19
-rw-r--r--usr/src/tools/smatch/src/validation/optim/ext-trunc-smaller.c18
-rw-r--r--usr/src/tools/smatch/src/validation/optim/fpcast-constant.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/inline-return.c24
-rw-r--r--usr/src/tools/smatch/src/validation/optim/kill-casts.c (renamed from usr/src/tools/smatch/src/validation/kill-casts.c)5
-rw-r--r--usr/src/tools/smatch/src/validation/optim/kill-stores0.c34
-rw-r--r--usr/src/tools/smatch/src/validation/optim/kill-stores1.c48
-rw-r--r--usr/src/tools/smatch/src/validation/optim/kill-stores2.c17
-rw-r--r--usr/src/tools/smatch/src/validation/optim/killed-insn.c14
-rw-r--r--usr/src/tools/smatch/src/validation/optim/live-stores0.c29
-rw-r--r--usr/src/tools/smatch/src/validation/optim/load-converted.c14
-rw-r--r--usr/src/tools/smatch/src/validation/optim/load-dead.c11
-rw-r--r--usr/src/tools/smatch/src/validation/optim/load-semi-volatile.c24
-rw-r--r--usr/src/tools/smatch/src/validation/optim/lsr-and0.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/lsr-and1.c18
-rw-r--r--usr/src/tools/smatch/src/validation/optim/lsr-asr.c42
-rw-r--r--usr/src/tools/smatch/src/validation/optim/lsr-shl0.c14
-rw-r--r--usr/src/tools/smatch/src/validation/optim/mask-lsr.c14
-rw-r--r--usr/src/tools/smatch/src/validation/optim/mask-out.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/mask1-setne0.c28
-rw-r--r--usr/src/tools/smatch/src/validation/optim/missing-select.c23
-rw-r--r--usr/src/tools/smatch/src/validation/optim/muldiv-minus-one.c2
-rw-r--r--usr/src/tools/smatch/src/validation/optim/null-phi.c9
-rw-r--r--usr/src/tools/smatch/src/validation/optim/or-and-constant1.c29
-rw-r--r--usr/src/tools/smatch/src/validation/optim/phi-ret.c21
-rw-r--r--usr/src/tools/smatch/src/validation/optim/restrict.c73
-rw-r--r--usr/src/tools/smatch/src/validation/optim/select-zero.c16
-rw-r--r--usr/src/tools/smatch/src/validation/optim/setcc-mask.c18
-rw-r--r--usr/src/tools/smatch/src/validation/optim/setne0-sext.c9
-rw-r--r--usr/src/tools/smatch/src/validation/optim/setne0-trunc.c9
-rw-r--r--usr/src/tools/smatch/src/validation/optim/setne0-zext.c9
-rw-r--r--usr/src/tools/smatch/src/validation/optim/sext-sext.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/sext.c15
-rw-r--r--usr/src/tools/smatch/src/validation/optim/sh-or-and0.c20
-rw-r--r--usr/src/tools/smatch/src/validation/optim/sh-or-and1.c20
-rw-r--r--usr/src/tools/smatch/src/validation/optim/sh-or-and2.c21
-rw-r--r--usr/src/tools/smatch/src/validation/optim/shift-big.c82
-rw-r--r--usr/src/tools/smatch/src/validation/optim/shift-shift.c149
-rw-r--r--usr/src/tools/smatch/src/validation/optim/shift-zext.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/shl-and0.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/shl-and1.c18
-rw-r--r--usr/src/tools/smatch/src/validation/optim/shl-lsr0.c14
-rw-r--r--usr/src/tools/smatch/src/validation/optim/store-dominated.c15
-rw-r--r--usr/src/tools/smatch/src/validation/optim/trivial-phis.c14
-rw-r--r--usr/src/tools/smatch/src/validation/optim/trunc-mask-zext.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/trunc-or-shl.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/trunc-seteq0.c18
-rw-r--r--usr/src/tools/smatch/src/validation/optim/trunc-setne0.c20
-rw-r--r--usr/src/tools/smatch/src/validation/optim/trunc-trunc.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/volatile-bitfield.c16
-rw-r--r--usr/src/tools/smatch/src/validation/optim/volatile-side-effect.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/volatile-store00.c27
-rw-r--r--usr/src/tools/smatch/src/validation/optim/zext-and.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/zext-and1.c12
-rw-r--r--usr/src/tools/smatch/src/validation/optim/zext-asr.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/zext-sext.c13
-rw-r--r--usr/src/tools/smatch/src/validation/optim/zext-zext.c13
-rw-r--r--usr/src/tools/smatch/src/validation/option-parsing-00.c5
-rw-r--r--usr/src/tools/smatch/src/validation/option-parsing-01.c5
-rw-r--r--usr/src/tools/smatch/src/validation/overflow.c19
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/base-file.c17
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/base-file.h2
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/builtin.c17
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/cli-D-arg.c12
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/cli-D-space.c10
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/dump-macros-empty.c2
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/dump-macros-multi.c2
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/dump-macros-only.c36
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/dump-macros.c18
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/dynamic.c37
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/extra-token.c15
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/has-attribute.c56
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/has-builtin.c43
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/ident-pragma.c12
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/ident.c12
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/include-level.c14
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/include-level.h1
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/missing-delim.c17
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/phase2-backslash.c (renamed from usr/src/tools/smatch/src/validation/phase2/backslash)29
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/phase3-comments.c (renamed from usr/src/tools/smatch/src/validation/phase3/comments)12
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/predef-char-bit.c16
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/predef-llp64.c9
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/predef-lp32.c9
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/predef-lp64.c9
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/predef-max.c18
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/predef-sizeof.c25
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/predef-unsigned.c9
-rw-r--r--usr/src/tools/smatch/src/validation/preprocessor/predef.c57
-rw-r--r--usr/src/tools/smatch/src/validation/ptr-inherit.c10
-rw-r--r--usr/src/tools/smatch/src/validation/ptr-sub-blows.c23
-rw-r--r--usr/src/tools/smatch/src/validation/range-syntax.c23
-rw-r--r--usr/src/tools/smatch/src/validation/repeat.h24
-rw-r--r--usr/src/tools/smatch/src/validation/reserved.c4
-rw-r--r--usr/src/tools/smatch/src/validation/restrict.c93
-rw-r--r--usr/src/tools/smatch/src/validation/self-quote-args.c7
-rw-r--r--usr/src/tools/smatch/src/validation/shift-negative.c17
-rw-r--r--usr/src/tools/smatch/src/validation/shift-undef-long.c21
-rw-r--r--usr/src/tools/smatch/src/validation/shift-undef.c164
-rw-r--r--usr/src/tools/smatch/src/validation/sizeof-bool.c2
-rw-r--r--usr/src/tools/smatch/src/validation/sizeof-builtin.c15
-rw-r--r--usr/src/tools/smatch/src/validation/sizeof-function.c49
-rw-r--r--usr/src/tools/smatch/src/validation/sizeof-incomplete-type.c27
-rw-r--r--usr/src/tools/smatch/src/validation/sm_compare18.c24
-rw-r--r--usr/src/tools/smatch/src/validation/storage-struct-member.c20
-rw-r--r--usr/src/tools/smatch/src/validation/struct-as.c2
-rw-r--r--usr/src/tools/smatch/src/validation/switch-long.c47
-rw-r--r--usr/src/tools/smatch/src/validation/test-be.c46
-rwxr-xr-xusr/src/tools/smatch/src/validation/test-suite378
-rw-r--r--usr/src/tools/smatch/src/validation/testsuite-selfcheck1.c10
-rw-r--r--usr/src/tools/smatch/src/validation/testsuite-selfcheck2.c10
-rw-r--r--usr/src/tools/smatch/src/validation/testsuite-selfcheck3.c10
-rw-r--r--usr/src/tools/smatch/src/validation/type-compare.c76
-rw-r--r--usr/src/tools/smatch/src/validation/typedef-redef-c89.c13
-rw-r--r--usr/src/tools/smatch/src/validation/typedef-redef.c13
-rw-r--r--usr/src/tools/smatch/src/validation/typedef_shadow.c4
-rw-r--r--usr/src/tools/smatch/src/validation/typediff-arraysize.c12
-rw-r--r--usr/src/tools/smatch/src/validation/typediff-enum.c34
-rw-r--r--usr/src/tools/smatch/src/validation/typeof-bad.c17
-rw-r--r--usr/src/tools/smatch/src/validation/typeof-mods.c28
-rw-r--r--usr/src/tools/smatch/src/validation/var-undef-partial.c20
-rw-r--r--usr/src/tools/smatch/src/validation/vla-sizeof-ice.c19
-rw-r--r--usr/src/tools/smatch/src/validation/vla-sizeof.c37
-rw-r--r--usr/src/tools/smatch/src/validation/vla-sizeof0.c20
-rw-r--r--usr/src/tools/smatch/src/validation/vla-sizeof1.c21
-rw-r--r--usr/src/tools/smatch/src/validation/vla-sizeof2.c21
-rw-r--r--usr/src/tools/smatch/src/validation/vla-sizeof3.c21
-rw-r--r--usr/src/tools/smatch/src/validation/vla-sizeof4.c22
-rw-r--r--usr/src/uts/common/io/mac/mac.c2
-rw-r--r--usr/src/uts/intel/mac/Makefile5
-rw-r--r--usr/src/uts/intel/procfs/Makefile5
-rw-r--r--usr/src/uts/intel/sol_ofs/Makefile6
-rw-r--r--usr/src/uts/intel/sol_uverbs/Makefile5
-rw-r--r--usr/src/uts/intel/zfs/Makefile3
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 = ' ' . &quote_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, &ltype);
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(&macros, 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.