summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/boot/Makefile.version2
-rw-r--r--usr/src/boot/sys/boot/common/tem.c11
-rw-r--r--usr/src/cmd/auditreduce/Makefile6
-rw-r--r--usr/src/cmd/auditreduce/main.c22
-rw-r--r--usr/src/common/font/font.c6
-rw-r--r--usr/src/lib/libefi/common/mapfile-vers5
-rw-r--r--usr/src/lib/libefi/common/rdwr_efi.c21
-rw-r--r--usr/src/lib/libproc/common/Psymtab.c8
-rw-r--r--usr/src/lib/udapl/libdat/Makefile.com12
-rw-r--r--usr/src/man/man3ext/Makefile46
-rw-r--r--usr/src/man/man3ext/efi_alloc_and_init.3ext31
-rw-r--r--usr/src/man/man3lib/libefi.3lib16
-rw-r--r--usr/src/pkg/manifests/system-library.man3ext.inc2
-rw-r--r--usr/src/test/libc-tests/runfiles/default.run4
-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/common/io/tem_safe.c4
-rw-r--r--usr/src/uts/common/sys/efi_partition.h1
-rw-r--r--usr/src/uts/common/sys/rgb.h5
-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
655 files changed, 23034 insertions, 6237 deletions
diff --git a/usr/src/boot/Makefile.version b/usr/src/boot/Makefile.version
index 46e00fbcea..dbae2fd094 100644
--- a/usr/src/boot/Makefile.version
+++ b/usr/src/boot/Makefile.version
@@ -33,4 +33,4 @@ LOADER_VERSION = 1.1
# Use date like formatting here, YYYY.MM.DD.XX, without leading zeroes.
# The version is processed from left to right, the version number can only
# be increased.
-BOOT_VERSION = $(LOADER_VERSION)-2019.11.06.1
+BOOT_VERSION = $(LOADER_VERSION)-2019.11.15.1
diff --git a/usr/src/boot/sys/boot/common/tem.c b/usr/src/boot/sys/boot/common/tem.c
index ef21b47085..9f4a852286 100644
--- a/usr/src/boot/sys/boot/common/tem.c
+++ b/usr/src/boot/sys/boot/common/tem.c
@@ -2815,23 +2815,22 @@ tem_get_attr(struct tem_vt_state *tem, text_color_t *fg,
static void
tem_get_color(text_color_t *fg, text_color_t *bg, term_char_t c)
{
- if (c.tc_fg_color < 16) {
+ *fg = c.tc_fg_color;
+ *bg = c.tc_bg_color;
+
+ if (c.tc_fg_color < XLATE_NCOLORS) {
if (TEM_CHAR_ATTR(c.tc_char) &
(TEM_ATTR_BRIGHT_FG | TEM_ATTR_BOLD))
*fg = brt_xlate[c.tc_fg_color];
else
*fg = dim_xlate[c.tc_fg_color];
- } else {
- *fg = c.tc_fg_color;
}
- if (c.tc_bg_color < 16) {
+ if (c.tc_bg_color < XLATE_NCOLORS) {
if (TEM_CHAR_ATTR(c.tc_char) & TEM_ATTR_BRIGHT_BG)
*bg = brt_xlate[c.tc_bg_color];
else
*bg = dim_xlate[c.tc_bg_color];
- } else {
- *bg = c.tc_bg_color;
}
}
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/common/font/font.c b/usr/src/common/font/font.c
index 501e8ec934..3556f27cf8 100644
--- a/usr/src/common/font/font.c
+++ b/usr/src/common/font/font.c
@@ -48,9 +48,9 @@
/* ANSI color to sun color translation. */
/* BEGIN CSTYLED */
-/* Bk Rd Gr Br Bl Mg Cy Wh */
-const uint8_t dim_xlate[] = { 1, 5, 3, 7, 2, 6, 4, 8 };
-const uint8_t brt_xlate[] = { 9, 13, 11, 15, 10, 14, 12, 0 };
+/* Bk Rd Gr Br Bl Mg Cy Wh */
+const uint8_t dim_xlate[XLATE_NCOLORS] = { 1, 5, 3, 7, 2, 6, 4, 8 };
+const uint8_t brt_xlate[XLATE_NCOLORS] = { 9, 13, 11, 15, 10, 14, 12, 0 };
const uint8_t solaris_color_to_pc_color[16] = {
pc_brt_white, /* 0 - brt_white */
diff --git a/usr/src/lib/libefi/common/mapfile-vers b/usr/src/lib/libefi/common/mapfile-vers
index 84a908ef93..43a41ccd08 100644
--- a/usr/src/lib/libefi/common/mapfile-vers
+++ b/usr/src/lib/libefi/common/mapfile-vers
@@ -38,6 +38,11 @@
$mapfile_version 2
+SYMBOL_VERSION ILLUMOS_0.1 {
+ global:
+ efi_reserved_sectors;
+} SUNW_1.2;
+
SYMBOL_VERSION SUNW_1.2 {
global:
efi_use_whole_disk;
diff --git a/usr/src/lib/libefi/common/rdwr_efi.c b/usr/src/lib/libefi/common/rdwr_efi.c
index 6aaaba7b92..74f65378b1 100644
--- a/usr/src/lib/libefi/common/rdwr_efi.c
+++ b/usr/src/lib/libefi/common/rdwr_efi.c
@@ -240,6 +240,19 @@ read_disk_info(int fd, diskaddr_t *capacity, uint_t *lbsize)
#define MAX_PARTS ((4294967295UL - sizeof (struct dk_gpt)) / \
sizeof (struct dk_part))
+/*
+ * The EFI reserved partition size is 8 MiB. This calculates the number of
+ * sectors required to store 8 MiB, taking into account the device's sector
+ * size.
+ */
+uint_t
+efi_reserved_sectors(dk_gpt_t *efi)
+{
+ /* roundup to sector size */
+ return ((EFI_MIN_RESV_SIZE * DEV_BSIZE + efi->efi_lbasize - 1) /
+ efi->efi_lbasize);
+}
+
int
efi_alloc_and_init(int fd, uint32_t nparts, struct dk_gpt **vtoc)
{
@@ -1013,7 +1026,7 @@ efi_use_whole_disk(int fd)
* physically non-zero partition.
*/
if (pl_start + pl_size - 1 == efi_label->efi_last_u_lba -
- EFI_MIN_RESV_SIZE) {
+ efi_reserved_sectors(efi_label)) {
efi_label->efi_parts[phy_last_slice].p_size +=
efi_label->efi_last_lba - efi_label->efi_altern_lba;
}
@@ -1270,10 +1283,12 @@ efi_err_check(struct dk_gpt *vtoc)
int i, j;
diskaddr_t istart, jstart, isize, jsize, endsect;
int overlap = 0;
+ uint_t reserved;
/*
* make sure no partitions overlap
*/
+ reserved = efi_reserved_sectors(vtoc);
for (i = 0; i < vtoc->efi_nparts; i++) {
/* It can't be unassigned and have an actual size */
if ((vtoc->efi_parts[i].p_tag == V_UNASSIGNED) &&
@@ -1292,10 +1307,10 @@ efi_err_check(struct dk_gpt *vtoc)
"%d\n", i);
}
resv_part = i;
- if (vtoc->efi_parts[i].p_size != EFI_MIN_RESV_SIZE)
+ if (vtoc->efi_parts[i].p_size != reserved)
(void) fprintf(stderr,
"Warning: reserved partition size must "
- "be %d sectors\n", EFI_MIN_RESV_SIZE);
+ "be %u sectors\n", reserved);
}
if ((vtoc->efi_parts[i].p_start < vtoc->efi_first_u_lba) ||
(vtoc->efi_parts[i].p_start > vtoc->efi_last_u_lba)) {
diff --git a/usr/src/lib/libproc/common/Psymtab.c b/usr/src/lib/libproc/common/Psymtab.c
index a224dd4055..3715d9b900 100644
--- a/usr/src/lib/libproc/common/Psymtab.c
+++ b/usr/src/lib/libproc/common/Psymtab.c
@@ -3355,7 +3355,7 @@ Psymbol_iter_by_lmid(struct ps_prochandle *P, Lmid_t lmid,
const char *object_name, int which, int mask, proc_sym_f *func, void *cd)
{
return (Psymbol_iter_com(P, lmid, object_name, which, mask,
- PRO_NATURAL, (proc_xsym_f *)func, cd));
+ PRO_NATURAL, (proc_xsym_f *)(uintptr_t)func, cd));
}
int
@@ -3363,7 +3363,7 @@ Psymbol_iter(struct ps_prochandle *P,
const char *object_name, int which, int mask, proc_sym_f *func, void *cd)
{
return (Psymbol_iter_com(P, PR_LMID_EVERY, object_name, which, mask,
- PRO_NATURAL, (proc_xsym_f *)func, cd));
+ PRO_NATURAL, (proc_xsym_f *)(uintptr_t)func, cd));
}
int
@@ -3371,7 +3371,7 @@ Psymbol_iter_by_addr(struct ps_prochandle *P,
const char *object_name, int which, int mask, proc_sym_f *func, void *cd)
{
return (Psymbol_iter_com(P, PR_LMID_EVERY, object_name, which, mask,
- PRO_BYADDR, (proc_xsym_f *)func, cd));
+ PRO_BYADDR, (proc_xsym_f *)(uintptr_t)func, cd));
}
int
@@ -3379,7 +3379,7 @@ Psymbol_iter_by_name(struct ps_prochandle *P,
const char *object_name, int which, int mask, proc_sym_f *func, void *cd)
{
return (Psymbol_iter_com(P, PR_LMID_EVERY, object_name, which, mask,
- PRO_BYNAME, (proc_xsym_f *)func, cd));
+ PRO_BYNAME, (proc_xsym_f *)(uintptr_t)func, cd));
}
/*
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/man/man3ext/Makefile b/usr/src/man/man3ext/Makefile
index b26fb0879e..0f10a18e54 100644
--- a/usr/src/man/man3ext/Makefile
+++ b/usr/src/man/man3ext/Makefile
@@ -16,28 +16,28 @@
include $(SRC)/Makefile.master
-MANSECT= 3ext
-
-MANFILES= NOTE.3ext \
- SUNW_C_GetMechSession.3ext \
- auto_ef.3ext \
- crypt.3ext \
- demangle.3ext \
- ecb_crypt.3ext \
- efi_alloc_and_init.3ext \
- ld_support.3ext \
- md4.3ext \
- md5.3ext \
- read_vtoc.3ext \
- rtld_audit.3ext \
- rtld_db.3ext \
- sendfile.3ext \
- sendfilev.3ext \
- sha1.3ext \
- sha2.3ext \
- stdarg.3ext \
- tsalarm_get.3ext \
- varargs.3ext
+MANSECT= 3ext
+
+MANFILES= NOTE.3ext \
+ SUNW_C_GetMechSession.3ext \
+ auto_ef.3ext \
+ crypt.3ext \
+ demangle.3ext \
+ ecb_crypt.3ext \
+ efi_alloc_and_init.3ext \
+ ld_support.3ext \
+ md4.3ext \
+ md5.3ext \
+ read_vtoc.3ext \
+ rtld_audit.3ext \
+ rtld_db.3ext \
+ sendfile.3ext \
+ sendfilev.3ext \
+ sha1.3ext \
+ sha2.3ext \
+ stdarg.3ext \
+ tsalarm_get.3ext \
+ varargs.3ext
MANLINKS= DES_FAILED.3ext \
MD4Final.3ext \
@@ -77,6 +77,7 @@ MANLINKS= DES_FAILED.3ext \
des_setparity.3ext \
efi_alloc_and_read.3ext \
efi_free.3ext \
+ efi_reserved_sectors.3ext \
efi_use_whole_disk.3ext \
efi_write.3ext \
encrypt.3ext \
@@ -154,6 +155,7 @@ des_setparity.3ext := LINKSRC = ecb_crypt.3ext
efi_alloc_and_read.3ext := LINKSRC = efi_alloc_and_init.3ext
efi_free.3ext := LINKSRC = efi_alloc_and_init.3ext
+efi_reserved_sectors.3ext := LINKSRC = efi_alloc_and_init.3ext
efi_use_whole_disk.3ext := LINKSRC = efi_alloc_and_init.3ext
efi_write.3ext := LINKSRC = efi_alloc_and_init.3ext
diff --git a/usr/src/man/man3ext/efi_alloc_and_init.3ext b/usr/src/man/man3ext/efi_alloc_and_init.3ext
index 2f201b5846..bd7bb22af6 100644
--- a/usr/src/man/man3ext/efi_alloc_and_init.3ext
+++ b/usr/src/man/man3ext/efi_alloc_and_init.3ext
@@ -4,12 +4,11 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH EFI_ALLOC_AND_INIT 3EXT "February 6, 2018"
+.TH EFI_ALLOC_AND_INIT 3EXT "November 20, 2019"
.SH NAME
-efi_alloc_and_init, efi_alloc_and_read, efi_free, efi_write, efi_use_whole_disk
-\- manipulate a disk's EFI Partition Table
+efi_alloc_and_init, efi_alloc_and_read, efi_free, efi_write, efi_use_whole_disk,
+efi_reserved_sectors \- manipulate a disk's EFI Partition Table
.SH SYNOPSIS
-.LP
.nf
cc [ \fIflag \&.\|.\|.\fR ] \fIfile\fR\&.\|.\|. \fB-lefi\fR [ \fIlibrary \&.\|.\|.\fR ]
#include <sys/vtoc.h>
@@ -38,8 +37,12 @@ cc [ \fIflag \&.\|.\|.\fR ] \fIfile\fR\&.\|.\|. \fB-lefi\fR [ \fIlibrary \&.\|.\
\fBint\fR \fBefi_use_whole_disk\fR(\fBint\fR \fIfd\fR);
.fi
-.SH DESCRIPTION
.LP
+.nf
+\fBuint_t\fR \fBefi_reserved_sectors\fR(\fBdk_gpt_t *\fR\fIvtoc\fR);
+.fi
+
+.SH DESCRIPTION
The \fBefi_alloc_and_init()\fR function initializes the \fBdk_gpt_t\fR
structure specified by \fIvtoc\fR in preparation for a call to
\fBefi_write()\fR. It calculates and initializes the \fBefi_version\fR,
@@ -65,6 +68,14 @@ in the disk label and adds it to the last physically non-zero area before the
reserved slice (from slice 0 to slice 6 or unallocated space).
.sp
.LP
+The \fBefi_reserved_sectors()\fR function calculates number of sectors
+needed to create the reserved partition. The reserved partition is used
+by the operating system for internal purposes. The sector size used is
+based on the device and is recorded in the \fBefi_lbasize\fR member of
+the \fBdkgpt_t\fR structure indicated by the \fIvtoc\fR argument.
+A full description of the \fBdk_gpt_t\fR structure appears later in the manual.
+.sp
+.LP
The \fIfd\fR argument refers to any slice on a raw disk, opened with
\fBO_NDELAY\fR. See \fBopen\fR(2).
.sp
@@ -88,7 +99,6 @@ struct dk_part efi_parts[]; /* array of partitions */
.in -2
.SS "Protective Master Boot Record"
-.LP
When a disk receives an EFI label, a protective MBR (\fBPMBR\fR) is also
written containing a single partiton of type \fBEEh\fR and spanning the
entire disk (up to the limit of what can be represented in an MBR). By
@@ -100,7 +110,6 @@ firmware bugs, refer to \fB/usr/share/hwdata/efi.fixes\fR for more
information.
.SH RETURN VALUES
-.LP
Upon successful completion, \fBefi_alloc_and_init()\fR returns 0. Otherwise it
returns \fBVT_EIO\fR if an I/O operation to the disk fails.
.sp
@@ -137,6 +146,11 @@ An EFI label was not found.
.sp
.LP
+The \fBefi_reserved_sectors()\fR function always returns the number of
+reserved sectors required. It will always succeed.
+
+.sp
+.LP
Upon successful completion, \fBefi_write()\fR returns 0. Otherwise, it returns
a negative integer to indicate one of the following:
.sp
@@ -207,14 +221,12 @@ Space out of label was not found.
.RE
.SH USAGE
-.LP
The EFI label is used on disks with more than 1^32-1 blocks. For compatibility
reasons, the \fBread_vtoc\fR(3EXT) and \fBwrite_vtoc()\fR functions should be
used on smaller disks. The application should attempt the \fBread_vtoc()\fR or
\fBwrite_vtoc()\fR call, check for an error of \fBVT_ENOTSUP\fR, then call the
analogous EFI function.
.SH ATTRIBUTES
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -231,7 +243,6 @@ MT-Level Unsafe
.TE
.SH SEE ALSO
-.LP
\fBfmthard\fR(1M), \fBformat\fR(1M), \fBprtvtoc\fR(1M), \fBioctl\fR(2),
\fBopen\fR(2), \fBlibefi\fR(3LIB), \fBread_vtoc\fR(3EXT), \fBattributes\fR(5),
\fBdkio\fR(7I)
diff --git a/usr/src/man/man3lib/libefi.3lib b/usr/src/man/man3lib/libefi.3lib
index 2d7de1002e..a42e3c26a7 100644
--- a/usr/src/man/man3lib/libefi.3lib
+++ b/usr/src/man/man3lib/libefi.3lib
@@ -3,22 +3,17 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH LIBEFI 3LIB "May 8, 2008"
+.TH LIBEFI 3LIB "November 20, 2019"
.SH NAME
libefi \- EFI partition table library
.SH SYNOPSIS
-.LP
.nf
cc [ \fIflag\fR... ] \fIfile\fR... \fB-lefi\fR [ \fIlibrary\fR... ]
.fi
.SH DESCRIPTION
-.sp
-.LP
The functions in this library manipulate a disk's EFI partition table.
.SH INTERFACES
-.sp
-.LP
The shared object \fBlibefi.so.1\fR provides the public interfaces defined
below. See \fBIntro\fR(3) for additional information on shared object
interfaces.
@@ -29,12 +24,11 @@ interfaces.
l l
l l .
\fBefi_alloc_and_init\fR \fBefi_alloc_and_read\fR
-\fBefi_free\fR \fBefi_use_whole_disk\fR
-\fBefi_write\fR
+\fBefi_free\fR \fBefi_reserved_sectors\fR
+\fBefi_use_whole_disk\fR \fBefi_write\fR
.TE
.SH FILES
-.sp
.ne 2
.na
\fB\fB/lib/libefi.so.1\fR\fR
@@ -53,8 +47,6 @@ shared object
.RE
.SH ATTRIBUTES
-.sp
-.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -71,6 +63,4 @@ MT-Level Unsafe
.TE
.SH SEE ALSO
-.sp
-.LP
\fBIntro\fR(3), \fBefi_alloc_and_init\fR(3EXT), \fBattributes\fR(5)
diff --git a/usr/src/pkg/manifests/system-library.man3ext.inc b/usr/src/pkg/manifests/system-library.man3ext.inc
index a774b104b3..12e6950a7e 100644
--- a/usr/src/pkg/manifests/system-library.man3ext.inc
+++ b/usr/src/pkg/manifests/system-library.man3ext.inc
@@ -74,6 +74,8 @@ link path=usr/share/man/man3ext/des_setparity.3ext target=ecb_crypt.3ext
link path=usr/share/man/man3ext/efi_alloc_and_read.3ext \
target=efi_alloc_and_init.3ext
link path=usr/share/man/man3ext/efi_free.3ext target=efi_alloc_and_init.3ext
+link path=usr/share/man/man3ext/efi_reserved_sectors.3ext \
+ target=efi_alloc_and_init.3ext
link path=usr/share/man/man3ext/efi_use_whole_disk.3ext \
target=efi_alloc_and_init.3ext
link path=usr/share/man/man3ext/efi_write.3ext target=efi_alloc_and_init.3ext
diff --git a/usr/src/test/libc-tests/runfiles/default.run b/usr/src/test/libc-tests/runfiles/default.run
index 0e8a5d4163..c2d08b54bc 100644
--- a/usr/src/test/libc-tests/runfiles/default.run
+++ b/usr/src/test/libc-tests/runfiles/default.run
@@ -12,14 +12,14 @@
#
# Copyright (c) 2012 by Delphix. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
-# Copyright 2018 Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
#
[DEFAULT]
pre =
verbose = False
quiet = False
-timeout = 60
+timeout = 120
post =
outputdir = /var/tmp/test_results
diff --git a/usr/src/tools/smatch/Makefile b/usr/src/tools/smatch/Makefile
index 5c7bdba170..1265cf4190 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
@@ -60,78 +60,168 @@ CPPFLAGS += -I/usr/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 28688e7ea9..f4074a2b91 100644
--- a/usr/src/uts/common/io/mac/mac.c
+++ b/usr/src/uts/common/io/mac/mac.c
@@ -5234,7 +5234,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/common/io/tem_safe.c b/usr/src/uts/common/io/tem_safe.c
index 65c2635959..572f9c16bd 100644
--- a/usr/src/uts/common/io/tem_safe.c
+++ b/usr/src/uts/common/io/tem_safe.c
@@ -2375,7 +2375,7 @@ tem_safe_get_color(text_color_t *fg, text_color_t *bg, term_char_t c)
*fg = c.tc_fg_color;
*bg = c.tc_bg_color;
- if (c.tc_fg_color < 16) {
+ if (c.tc_fg_color < XLATE_NCOLORS) {
if (TEM_ATTR_ISSET(c.tc_char,
TEM_ATTR_BRIGHT_FG | TEM_ATTR_BOLD))
*fg = brt_xlate[c.tc_fg_color];
@@ -2383,7 +2383,7 @@ tem_safe_get_color(text_color_t *fg, text_color_t *bg, term_char_t c)
*fg = dim_xlate[c.tc_fg_color];
}
- if (c.tc_bg_color < 16) {
+ if (c.tc_bg_color < XLATE_NCOLORS) {
if (TEM_ATTR_ISSET(c.tc_char, TEM_ATTR_BRIGHT_BG))
*bg = brt_xlate[c.tc_bg_color];
else
diff --git a/usr/src/uts/common/sys/efi_partition.h b/usr/src/uts/common/sys/efi_partition.h
index 065f65f802..4061cb4ce9 100644
--- a/usr/src/uts/common/sys/efi_partition.h
+++ b/usr/src/uts/common/sys/efi_partition.h
@@ -255,6 +255,7 @@ struct partition64 {
#define EFI_NUMPAR 9
#ifndef _KERNEL
+extern uint_t efi_reserved_sectors(struct dk_gpt *);
extern int efi_alloc_and_init(int, uint32_t, struct dk_gpt **);
extern int efi_alloc_and_read(int, struct dk_gpt **);
extern int efi_write(int, struct dk_gpt *);
diff --git a/usr/src/uts/common/sys/rgb.h b/usr/src/uts/common/sys/rgb.h
index d9b6aa161a..9ddfaa9e3f 100644
--- a/usr/src/uts/common/sys/rgb.h
+++ b/usr/src/uts/common/sys/rgb.h
@@ -67,8 +67,9 @@ typedef enum pc_colors {
pc_brt_white = 15
} pc_colors_t;
-extern const uint8_t dim_xlate[];
-extern const uint8_t brt_xlate[];
+#define XLATE_NCOLORS 8
+extern const uint8_t dim_xlate[XLATE_NCOLORS];
+extern const uint8_t brt_xlate[XLATE_NCOLORS];
extern const uint8_t solaris_color_to_pc_color[16];
extern uint32_t rgb_color_map(const rgb_t *, uint8_t);
diff --git a/usr/src/uts/intel/mac/Makefile b/usr/src/uts/intel/mac/Makefile
index 7b9fefec25..5b31b6d3ba 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).
@@ -66,11 +66,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.