https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60874
--- Comment #8 from Uroš Bizjak <ubizjak at gmail dot com> --- After lots of debugging... The problem is with the label that is passed as an argument to __go_set_defer_retaddr. In function main.$thunk0, in _.179r.cse1 dump, we have: ... (insn 7 3 8 2 (set (reg:DI 84) (high:DI (label_ref:DI 47))) rr.go:57 236 {*movdi} (insn_list:REG_LABEL_OPERAND 47 (nil))) (insn 8 7 9 2 (set (reg:DI 83) (lo_sum:DI (reg:DI 84) (label_ref:DI 47))) rr.go:57 230 {*movdi_er_low_l} (insn_list:REG_LABEL_OPERAND 47 (expr_list:REG_EQUAL (label_ref:DI 47) (nil)))) (insn 9 8 10 2 (set (reg:DI 16 $16) (reg:DI 83)) rr.go:57 236 {*movdi} (nil)) (call_insn 10 9 11 2 (parallel [ (set (reg:DI 0 $0) (call (mem:DI (symbol_ref:DI ("__go_set_defer_retaddr") [flags 0x41] <function_decl 0x2000066a900 __go_set_defer_retaddr>) [0 __go_set_defer_retaddr S8 A64]) (const_int 0 [0]))) (use (reg:DI 29 $29)) (clobber (reg:DI 26 $26)) ]) rr.go:57 357 {*call_value_osf_1_er} (expr_list:REG_CALL_DECL (symbol_ref:DI ("__go_set_defer_retaddr") [flags 0x41] <function_decl 0x2000066a900 __go_set_defer_retaddr>) (nil)) (expr_list:DI (use (reg:DI 16 $16)) (nil))) ... (call_insn 46 45 47 5 (parallel [ (call (mem:DI (reg/f:DI 80 [ D.1014 ]) [0 *_42 S8 A64]) (const_int 4048 [0xfd0])) (use (reg:DI 29 $29)) (clobber (reg:DI 26 $26)) ]) rr.go:57 210 {*call_osf_1_er} (expr_list:REG_DEAD (reg/f:DI 80 [ D.1014 ]) (expr_list:REG_DEAD (reg:DI 21 $21) (expr_list:REG_DEAD (reg:DI 20 $20) (expr_list:REG_DEAD (reg:DI 19 $19) (expr_list:REG_DEAD (reg:DI 18 $18) (expr_list:REG_DEAD (reg:DI 17 $17) (expr_list:REG_DEAD (reg:DI 16 $16) (expr_list:REG_CALL_DECL (nil) (nil))))))))) (expr_list (use (reg:DI 21 $21)) (expr_list (use (reg:DI 20 $20)) (expr_list (use (reg:DI 19 $19)) (expr_list (use (reg:DI 18 $18)) (expr_list (use (reg:DI 17 $17)) (expr_list (use (reg:DI 16 $16)) (expr_list:BLK (use (mem:BLK (reg/f:DI 30 $30) [0 S4048 A64])) (nil))))))))) ;; succ: 6 [100.0%] (FALLTHRU) ;; lr out 15 [$15] 26 [$26] 29 [$29] 30 [$30] 31 [AP] 63 [FP] ;; live out 15 [$15] 29 [$29] 30 [$30] 31 [AP] 63 [FP] ;; basic block 6, loop depth 0, count 0, freq 10000, maybe hot ;; prev block 5, next block 7, flags: (REACHABLE, RTL, MODIFIED) ;; pred: 2 [39.0%] ;; 5 [100.0%] (FALLTHRU) ;; bb 6 artificial_defs: { } ;; bb 6 artificial_uses: { u-1(15){ }u-1(29){ }u-1(30){ }u-1(31){ }u-1(63){ }} ;; lr in 15 [$15] 26 [$26] 29 [$29] 30 [$30] 31 [AP] 63 [FP] ;; lr use 15 [$15] 29 [$29] 30 [$30] 31 [AP] 63 [FP] ;; lr def 81 ;; live in 15 [$15] 29 [$29] 30 [$30] 31 [AP] 63 [FP] ;; live gen 81 ;; live kill (code_label/s 47 46 48 6 2 ("retaddr") [5 uses]) (note 48 47 49 6 [bb 6] NOTE_INSN_BASIC_BLOCK) (insn 49 48 52 6 (set (reg:DI 81 [ <retval> ]) (const_int 0 [0])) rr.go:57 236 {*movdi} (nil)) succ: 8 [100.0%] (FALLTHRU) Unfortunatelly, this is not a robust approach, since in a follow-up _.180r.fwprop1 pass (insn 49) propagates to function return value, leaving: (code_label/s 47 46 48 6 2 ("retaddr") [5 uses]) (note 48 47 52 6 [bb 6] NOTE_INSN_BASIC_BLOCK) ;; succ: 8 [100.0%] (FALLTHRU) The following 181r.cprop pass removes the label (also updates passed argument to __go_set_defer_retaddr in (insn 7) and (insn 8)) and merges bb after a thunk call: (insn 7 3 8 2 (set (reg/f:DI 84) (high:DI (label_ref:DI [47 deleted]))) rr.go:57 236 {*movdi} (insn_list:REG_LABEL_OPERAND 47 (nil))) (insn 8 7 9 2 (set (reg/f:DI 83) (lo_sum:DI (reg/f:DI 84) (label_ref:DI [47 deleted]))) rr.go:57 230 {*movdi_er_low_l} (expr_list:REG_DEAD (reg/f:DI 84) (insn_list:REG_LABEL_OPERAND 47 (expr_list:REG_EQUAL (label_ref:DI [47 deleted]) (nil))))) ... (call_insn 46 45 47 5 (parallel [ (call (mem:DI (reg/f:DI 80 [ D.1014 ]) [0 *_42 S8 A64]) (const_int 4048 [0xfd0])) (use (reg:DI 29 $29)) (clobber (reg:DI 26 $26)) ]) rr.go:57 210 {*call_osf_1_er} (expr_list:REG_DEAD (reg/f:DI 80 [ D.1014 ]) (expr_list:REG_DEAD (reg:DI 21 $21) (expr_list:REG_DEAD (reg:DI 20 $20) (expr_list:REG_DEAD (reg:DI 19 $19) (expr_list:REG_DEAD (reg:DI 18 $18) (expr_list:REG_DEAD (reg:DI 17 $17) (expr_list:REG_DEAD (reg:DI 16 $16) (expr_list:REG_CALL_DECL (nil) (nil))))))))) (expr_list (use (reg:DI 21 $21)) (expr_list (use (reg:DI 20 $20)) (expr_list (use (reg:DI 19 $19)) (expr_list (use (reg:DI 18 $18)) (expr_list (use (reg:DI 17 $17)) (expr_list (use (reg:DI 16 $16)) (expr_list:BLK (use (mem:BLK (reg/f:DI 30 $30) [0 S4048 A64])) (nil))))))))) (note/s 47 46 52 5 ("retaddr") NOTE_INSN_DELETED_LABEL 2) ;; succ: 8 [100.0%] (FALLTHRU) The missing label is substituted with a reference to some nearby label, resulting in: [+] ldah $16,$L8($29) !gprelhigh stq $26,4048($30) stq $9,4056($30) [+] lda $16,$L8($16) !gprellow jsr $26,($27),__go_set_defer_retaddr !lituse_jsr!22 ... $L10: $L8: ldq $27,memcpy($29) !literal!12 lda $17,56($9) mov $30,$16 lda $18,4048($31) jsr $26,($27),memcpy !lituse_jsr!12 ... [++] jsr $26,($27),0 ldah $29,0($26) !gpdisp!24 lda $29,0($29) !gpdisp!24 $L11: mov $31,$0 The [+] marks wrong label load and [++] marks thunk call. $L8 is an unrelated label that gets loaded as an argument to __go_set_defer_retaddr. Since $L8 is "far" away from thunk call, this fails the test in __go_can_recover (runtime/go-recover.c): dret = (const char *) d->__retaddr; if (ret <= dret && ret + 16 >= dret) return 1; Manually changing $L8 to $L11 in the call to __go_set_defer_retaddr (in an assembly) results in a successful test.