History

Fix some code suppression fallout

Some more subtle issues with code suppression: - outputting asms but not their operand setup is broken - but global asms must always be output - statement expressions are transparent to code suppression - vtop can't be transformed from VT_CMP/VT_JMP when nocode_wanted Also remove .exe files from tests2 if they don't fail.

Michael Matz committed 9 years ago

Commit 42e2a671 parent 559ee1e8 files · +138 −20Browse files

Unified · Split

8 files
  1. tccasm.c +16 −16
  2. tccgen.c +16 −3
  3. tests/tcctest.c +32 −0
  4. tests/tests2/85-asm-outside-function.c +2 −0
  5. tests/tests2/85-asm-outside-function.expect +1 −0
  6. tests/tests2/88_codeopt.c +68 −0
  7. tests/tests2/88_codeopt.expect +2 −0
  8. tests/tests2/Makefile +1 −1
tccasm.c+16−16
@@ -32,7 +32,7 @@ ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n) · 31 unmodified lines
32}32}
33 33
34ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);34ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
35static int tcc_assemble_internal(TCCState *s1, int do_preprocess);35static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
36static Sym sym_dot;36static Sym sym_dot;
37 37
38/* Return a symbol we can use inside the assembler, having name NAME.38/* Return a symbol we can use inside the assembler, having name NAME.
@@ -462,7 +462,7 @@ static void pop_section(TCCState *s1) · 423 unmodified lines
462 use_section1(s1, prev);462 use_section1(s1, prev);
463}463}
464 464
465static void asm_parse_directive(TCCState *s1)465static void asm_parse_directive(TCCState *s1, int global)
466{466{
467 int n, offset, v, size, tok1;467 int n, offset, v, size, tok1;
468 Section *sec;468 Section *sec;
@@ -644,7 +644,8 @@ static void asm_parse_directive(TCCState *s1) · 175 unmodified lines
644 save_parse_state(&saved_parse_state);644 save_parse_state(&saved_parse_state);
645 begin_macro(init_str, 1);645 begin_macro(init_str, 1);
646 while (repeat-- > 0) {646 while (repeat-- > 0) {
647 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS));647 tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
648 global);
648 macro_ptr = init_str->str;649 macro_ptr = init_str->str;
649 }650 }
650 end_macro();651 end_macro();
@@ -913,14 +914,10 @@ static void asm_parse_directive(TCCState *s1) · 262 unmodified lines
913 914
914 915
915/* assemble a file */916/* assemble a file */
916static int tcc_assemble_internal(TCCState *s1, int do_preprocess)917static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
917{918{
918 int saved_nocode_wanted;
919 int opcode;919 int opcode;
920 920
921 saved_nocode_wanted = nocode_wanted;
922 nocode_wanted = 0;
923
924 /* XXX: undefine C labels */921 /* XXX: undefine C labels */
925 922
926 ch = file->buf_ptr[0];923 ch = file->buf_ptr[0];
@@ -940,7 +937,7 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess) · 13 unmodified lines
940 while (tok != TOK_LINEFEED)937 while (tok != TOK_LINEFEED)
941 next();938 next();
942 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {939 } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
943 asm_parse_directive(s1);940 asm_parse_directive(s1, global);
944 } else if (tok == TOK_PPNUM) {941 } else if (tok == TOK_PPNUM) {
945 Sym *sym;942 Sym *sym;
946 const char *p;943 const char *p;
@@ -964,7 +961,7 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess) · 17 unmodified lines
964 /* handle "extern void vide(void); __asm__("vide: ret");" as961 /* handle "extern void vide(void); __asm__("vide: ret");" as
965 "__asm__("globl vide\nvide: ret");" */962 "__asm__("globl vide\nvide: ret");" */
966 Sym *sym = sym_find(opcode);963 Sym *sym = sym_find(opcode);
967 if (sym && (sym->type.t & VT_EXTERN) && saved_nocode_wanted) {964 if (sym && (sym->type.t & VT_EXTERN) && global) {
968 sym = label_find(opcode);965 sym = label_find(opcode);
969 if (!sym) {966 if (!sym) {
970 sym = label_push(&s1->asm_labels, opcode, 0);967 sym = label_push(&s1->asm_labels, opcode, 0);
@@ -993,7 +990,6 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess) · 22 unmodified lines
993 990
994 asm_free_labels(s1);991 asm_free_labels(s1);
995 992
996 nocode_wanted = saved_nocode_wanted;
997 return 0;993 return 0;
998}994}
999 995
@@ -1017,7 +1013,7 @@ ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess) · 17 unmodified lines
1017 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,1013 ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
1018 SHN_ABS, file->filename);1014 SHN_ABS, file->filename);
1019 1015
1020 ret = tcc_assemble_internal(s1, do_preprocess);1016 ret = tcc_assemble_internal(s1, do_preprocess, 1);
1021 1017
1022 cur_text_section->data_offset = ind;1018 cur_text_section->data_offset = ind;
1023 1019
@@ -1032,7 +1028,7 @@ ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess) · 8 unmodified lines
1032/* assemble the string 'str' in the current C compilation unit without1028/* assemble the string 'str' in the current C compilation unit without
1033 C preprocessing. NOTE: str is modified by modifying the '\0' at the1029 C preprocessing. NOTE: str is modified by modifying the '\0' at the
1034 end */1030 end */
1035static void tcc_assemble_inline(TCCState *s1, char *str, int len)1031static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
1036{1032{
1037 int saved_parse_flags;1033 int saved_parse_flags;
1038 const int *saved_macro_ptr;1034 const int *saved_macro_ptr;
@@ -1044,7 +1040,7 @@ static void tcc_assemble_inline(TCCState *s1, char *str, int len) · 5 unmodified lines
1044 memcpy(file->buffer, str, len);1040 memcpy(file->buffer, str, len);
1045 1041
1046 macro_ptr = NULL;1042 macro_ptr = NULL;
1047 tcc_assemble_internal(s1, 0);1043 tcc_assemble_internal(s1, 0, global);
1048 tcc_close();1044 tcc_close();
1049 1045
1050 parse_flags = saved_parse_flags;1046 parse_flags = saved_parse_flags;
@@ -1277,7 +1273,7 @@ ST_FUNC void asm_instr(void) · 226 unmodified lines
1277 clobber_regs, out_reg); 1273 clobber_regs, out_reg);
1278 1274
1279 /* assemble the string with tcc internal assembler */1275 /* assemble the string with tcc internal assembler */
1280 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);1276 tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
1281 1277
1282 /* restore the current C token */1278 /* restore the current C token */
1283 next();1279 next();
@@ -1299,7 +1295,10 @@ ST_FUNC void asm_instr(void) · 15 unmodified lines
1299ST_FUNC void asm_global_instr(void)1295ST_FUNC void asm_global_instr(void)
1300{1296{
1301 CString astr;1297 CString astr;
1298 int saved_nocode_wanted = nocode_wanted;
1302 1299
1300 /* Global asm blocks are always emitted. */
1301 nocode_wanted = 0;
1303 next();1302 next();
1304 parse_asm_str(&astr);1303 parse_asm_str(&astr);
1305 skip(')');1304 skip(')');
@@ -1315,7 +1314,7 @@ ST_FUNC void asm_global_instr(void) · 9 unmodified lines
1315 ind = cur_text_section->data_offset;1314 ind = cur_text_section->data_offset;
1316 1315
1317 /* assemble the string with tcc internal assembler */1316 /* assemble the string with tcc internal assembler */
1318 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);1317 tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
1319 1318
1320 cur_text_section->data_offset = ind;1319 cur_text_section->data_offset = ind;
1321 1320
@@ -1323,5 +1322,6 @@ ST_FUNC void asm_global_instr(void) · 1 unmodified line
1323 next();1322 next();
1324 1323
1325 cstr_free(&astr);1324 cstr_free(&astr);
1325 nocode_wanted = saved_nocode_wanted;
1326}1326}
1327#endif /* CONFIG_TCC_ASM */1327#endif /* CONFIG_TCC_ASM */
tccgen.c+16−3
@@ -575,8 +575,16 @@ static void vsetc(CType *type, int r, CValue *vc) · 574 unmodified lines
575 tcc_error("memory full (vstack)");575 tcc_error("memory full (vstack)");
576 /* cannot let cpu flags if other instruction are generated. Also576 /* cannot let cpu flags if other instruction are generated. Also
577 avoid leaving VT_JMP anywhere except on the top of the stack577 avoid leaving VT_JMP anywhere except on the top of the stack
578 because it would complicate the code generator. */578 because it would complicate the code generator.
579 if (vtop >= vstack) {579
580 Don't do this when nocode_wanted. vtop might come from
581 !nocode_wanted regions (see 88_codeopt.c) and transforming
582 it to a register without actually generating code is wrong
583 as their value might still be used for real. All values
584 we push under nocode_wanted will eventually be popped
585 again, so that the VT_CMP/VT_JMP value will be in vtop
586 when code is unsuppressed again. */
587 if (vtop >= vstack && !nocode_wanted) {
580 v = vtop->r & VT_VALMASK;588 v = vtop->r & VT_VALMASK;
581 if (v == VT_CMP || (v & ~1) == VT_JMP)589 if (v == VT_CMP || (v & ~1) == VT_JMP)
582 gv(RC_INT);590 gv(RC_INT);
@@ -4367,13 +4375,18 @@ ST_FUNC void unary(void) · 3784 unmodified lines
4367 gen_cast(&type);4375 gen_cast(&type);
4368 }4376 }
4369 } else if (tok == '{') {4377 } else if (tok == '{') {
4378 int saved_nocode_wanted = nocode_wanted;
4370 if (const_wanted)4379 if (const_wanted)
4371 tcc_error("expected constant");4380 tcc_error("expected constant");
4372 /* save all registers */4381 /* save all registers */
4373 save_regs(0);4382 save_regs(0);
4374 /* statement expression : we do not accept break/continue4383 /* statement expression : we do not accept break/continue
4375 inside as GCC does */4384 inside as GCC does. We do retain the nocode_wanted state,
4385 as statement expressions can't ever be entered from the
4386 outside, so any reactivation of code emission (from labels
4387 or loop heads) can be disabled again after the end of it. */
4376 block(NULL, NULL, 1);4388 block(NULL, NULL, 1);
4389 nocode_wanted = saved_nocode_wanted;
4377 skip(')');4390 skip(')');
4378 } else {4391 } else {
4379 gexpr();4392 gexpr();
tests/tcctest.c+32−0
@@ -1404,6 +1404,15 @@ void optimize_out(void) · 1403 unmodified lines
1404 if (defined_function() && 0)1404 if (defined_function() && 0)
1405 refer_to_undefined();1405 refer_to_undefined();
1406 1406
1407 if (0) {
1408 (void)sizeof( ({
1409 do { } while (0);
1410 0;
1411 }) );
1412 undefined_function();
1413 }
1414
1415 /* Leave the "if(1)return; printf()" in this order and last in the function */
1407 if (1)1416 if (1)
1408 return;1417 return;
1409 printf ("oor:%d\n", undefined_function());1418 printf ("oor:%d\n", undefined_function());
@@ -3251,6 +3260,28 @@ void trace_console(long len, long len2) · 1841 unmodified lines
3251 }3260 }
3252#endif3261#endif
3253}3262}
3263
3264void test_asm_dead_code(void)
3265{
3266 long rdi;
3267 /* Try to make sure that xdi contains a zero, and hence will
3268 lead to a segfault if the next asm is evaluated without
3269 arguments being set up. */
3270 asm volatile ("" : "=D" (rdi) : "0" (0));
3271 (void)sizeof (({
3272 int var;
3273 /* This shouldn't trigger a segfault, either the argument
3274 registers need to be set up and the asm emitted despite
3275 this being in an unevaluated context, or both the argument
3276 setup _and_ the asm emission need to be suppressed. The latter
3277 is better. Disabling asm code gen when suppression is on
3278 also fixes the above trace_console bug, but that came earlier
3279 than asm suppression. */
3280 asm volatile ("movl $0,(%0)" : : "D" (&var) : "memory");
3281 var;
3282 }));
3283}
3284
3254void asm_test(void)3285void asm_test(void)
3255{3286{
3256 char buf[128];3287 char buf[128];
@@ -3334,6 +3365,7 @@ void asm_test(void) · 77 unmodified lines
3334 printf ("regvar=%x\n", regvar);3365 printf ("regvar=%x\n", regvar);
3335 test_high_clobbers();3366 test_high_clobbers();
3336 trace_console(8, 8);3367 trace_console(8, 8);
3368 test_asm_dead_code();
3337 return;3369 return;
3338 label1:3370 label1:
3339 goto label2;3371 goto label2;
tests/tests2/85-asm-outside-function.c+2−0
@@ -1,7 +1,9 @@
1extern int printf (const char *, ...);
1extern void vide(void);2extern void vide(void);
2__asm__("vide: ret");3__asm__("vide: ret");
3 4
4int main() {5int main() {
5 vide();6 vide();
7 printf ("okay\n");
6 return 0;8 return 0;
7}9}
tests/tests2/85-asm-outside-function.expect+1−0
@@ -0,0 +1 @@
1okay
tests/tests2/88_codeopt.c+68−0
@@ -0,0 +1,68 @@
1/* Check some way in where code suppression caused various
2 miscompilations. */
3extern int printf (const char *, ...);
4typedef unsigned long size_t;
5
6size_t _brk_start, _brk_end;
7void * extend_brk(size_t size, size_t align)
8{
9 size_t mask = align - 1;
10 void *ret = 0;
11
12 do {
13 if (__builtin_expect(!!(_brk_start == 0), 0))
14 do {
15 printf("wrong1\n");
16 } while (0);
17 } while (0);
18 _brk_end = (_brk_end + mask) & ~mask;
19 ret = (void *)_brk_end;
20 _brk_end += size;
21
22 return ret;
23}
24
25static void get_args (int a, int b)
26{
27 if (a != 1)
28 printf("wrong2\n");
29 else
30 printf("okay\n");
31}
32
33void bla(void)
34{
35 int __ret = 42;
36 ({
37 if (__builtin_expect(!!(0), 0)) {
38 if (__builtin_expect(!!__ret, 0))
39 printf("wrong3\n");
40 int x = !!(__ret);
41 }
42 __ret;
43 });
44 get_args(!!__ret, sizeof(__ret));
45}
46
47_Bool chk(unsigned long addr, unsigned long limit, unsigned long size)
48{
49 _Bool ret;
50 /* This just needs to compile, no runtime test. (And it doesn't compile
51 only with certain internal checking added that's not committed). */
52 if (0)
53 ret = 0 != (!!(addr > limit - size));
54}
55
56int main()
57{
58 void *r;
59 _brk_start = 1024;
60 _brk_end = 1024;
61 r = extend_brk (4096, 16);
62 if (!r)
63 printf("wrong4\n");
64 else
65 printf("okay\n");
66 bla();
67 return 0;
68}
tests/tests2/88_codeopt.expect+2−0
@@ -0,0 +1,2 @@
1okay
2okay
tests/tests2/Makefile+1−1
@@ -59,7 +59,7 @@ all test: $(filter-out $(SKIP),$(TESTS)) · 58 unmodified lines
59 $(TCC) $< -o ./$*.exe $(FILTER) 2>&1 && \59 $(TCC) $< -o ./$*.exe $(FILTER) 2>&1 && \
60 ./$*.exe $(ARGS) >$*.output 2>&1 || true; \60 ./$*.exe $(ARGS) >$*.output 2>&1 || true; \
61 fi61 fi
62 @diff -Nbu $(SRC)/$*.expect $*.output && rm -f $*.output62 @diff -Nbu $(SRC)/$*.expect $*.output && rm -f $*.output $*.exe
63 63
64# automatically generate .expect files with gcc:64# automatically generate .expect files with gcc:
65%.expect : %.c65%.expect : %.c

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t