DISASSEMBLE shows no differences in assembly for different functions

Bug #1956870 reported by Michał "phoe" Herda
14
This bug affects 2 people
Affects Status Importance Assigned to Milestone
SBCL
Fix Released
Undecided
Unassigned

Bug Description

SBCL 2.1.11 on Linux amd64.

(define-symbol-macro %true-branches% ())

(defmacro with-compile-time-branching ((&rest branches) &body body
                                       &environment env)
  (cond ((not (null branches))
         (destructuring-bind (branch . other-branches) branches
           (let ((true-branches (macroexpand-1 '%true-branches% env)))
             `(if ,branch
                  (symbol-macrolet
                      ((%true-branches% (,branch . ,true-branches)))
                    (with-compile-time-branching (,@other-branches) ,@body))
                  (with-compile-time-branching (,@other-branches) ,@body)))))
        (t `(progn ,@body))))

(defmacro compile-time-if (branch then &optional else &environment env)
  (if (member branch (macroexpand-1 '%true-branches% env))
      then
      (or else `(progn))))

(sb-ext:restrict-compiler-policy 'speed 3 3)
(sb-ext:restrict-compiler-policy 'safety)
(sb-ext:restrict-compiler-policy 'debug)

(defun make-adder (x &key huge-p enormous-p)
  (with-compile-time-branching (huge-p enormous-p)
    (lambda (y) (+ x y
                   (compile-time-if huge-p (print 1000) 0) ;; note the PRINT call
                   (compile-time-if enormous-p 2000 0)))))

CL-USER> (disassemble (make-adder 10))
; disassembly for (LAMBDA (Y) :IN MAKE-ADDER)
; Size: 28 bytes. Origin: #x5370C788 ; (LAMBDA (Y)
                                                                  :IN
                                                                  MAKE-ADDER)
; 88: 488BD1 MOV RDX, RCX
; 8B: E8A0432FFF CALL #x52A00B30 ; GENERIC-+
; 90: 31FF XOR EDI, EDI
; 92: E899432FFF CALL #x52A00B30 ; GENERIC-+
; 97: 31FF XOR EDI, EDI
; 99: E892432FFF CALL #x52A00B30 ; GENERIC-+
; 9E: 488BE5 MOV RSP, RBP
; A1: F8 CLC
; A2: 5D POP RBP
; A3: C3 RET
NIL

CL-USER> (disassemble (make-adder 10 :huge-p t))
; disassembly for (LAMBDA (Y) :IN MAKE-ADDER)
; Size: 28 bytes. Origin: #x5370C788 ; (LAMBDA (Y)
                                                                  :IN
                                                                  MAKE-ADDER)
; 88: 488BD1 MOV RDX, RCX
; 8B: E8A0432FFF CALL #x52A00B30 ; GENERIC-+
; 90: 31FF XOR EDI, EDI
; 92: E899432FFF CALL #x52A00B30 ; GENERIC-+
; 97: 31FF XOR EDI, EDI
; 99: E892432FFF CALL #x52A00B30 ; GENERIC-+
; 9E: 488BE5 MOV RSP, RBP
; A1: F8 CLC
; A2: 5D POP RBP
; A3: C3 RET
NIL

(make-adder 10 :huge-p t) and (make-adder 10) return functions that behave differently, and yet the demonstrated assembly is the same and completely omits the PRINT call of one of the functions.

Revision history for this message
Michał "phoe" Herda (phoe-krk) wrote :

Smaller test case:

(defun make-adder (x &key huge-p)
  (if huge-p
      (lambda (y) (+ x y (print 1000)))
      (lambda (y) (+ x y 0))))

CL-USER> (disassemble (make-adder 10))
; disassembly for (LAMBDA (Y) :IN MAKE-ADDER)
; Size: 21 bytes. Origin: #x5370CB78 ; (LAMBDA (Y)
                                                                  :IN
                                                                  MAKE-ADDER)
; 78: 488BD1 MOV RDX, RCX
; 7B: E8B03F2FFF CALL #x52A00B30 ; GENERIC-+
; 80: 31FF XOR EDI, EDI
; 82: E8A93F2FFF CALL #x52A00B30 ; GENERIC-+
; 87: 488BE5 MOV RSP, RBP
; 8A: F8 CLC
; 8B: 5D POP RBP
; 8C: C3 RET
NIL

CL-USER> (disassemble (make-adder 10 :huge-p t))
; disassembly for (LAMBDA (Y) :IN MAKE-ADDER)
; Size: 21 bytes. Origin: #x5370CB78 ; (LAMBDA (Y)
                                                                  :IN
                                                                  MAKE-ADDER)
; 78: 488BD1 MOV RDX, RCX
; 7B: E8B03F2FFF CALL #x52A00B30 ; GENERIC-+
; 80: 31FF XOR EDI, EDI
; 82: E8A93F2FFF CALL #x52A00B30 ; GENERIC-+
; 87: 488BE5 MOV RSP, RBP
; 8A: F8 CLC
; 8B: 5D POP RBP
; 8C: C3 RET
NIL

Revision history for this message
Stas Boukarev (stassats) wrote :

It's a closure, which means the function is the same.

Revision history for this message
Michał "phoe" Herda (phoe-krk) wrote (last edit ):

I don't understand - what exactly is a closure here?

(defun make-adder (x &key huge-p)
  (if huge-p
      (lambda (y) (+ x y (print 1000)))
      (lambda (y) (+ x y 0))))

Here, based on the value of HUGE-P, one of two different functions should be returned - one of them calls PRINT and the other doesn't.

Revision history for this message
Stas Boukarev (stassats) wrote :

Closures share the code object with the parent function.

Revision history for this message
Michał "phoe" Herda (phoe-krk) wrote :
Download full text (4.3 KiB)

OK, I think I can understand that - do you mean that a single code object will, in this situation, contain executable code for both the parent function and the two closures that can be returned from it?

Asking because the DISASSEMBLE output is highly confusing to me. These two closures cannot possibly *execute* the same code, as it can be verified empirically. Even if the code is internally shared, I would expect at least *one* of the disassemblies to show a PRINT call - and that is not the case.

CL-USER> (defun make-adder (x &key huge-p)
           (if huge-p
               (lambda (y) (+ x y (print 1000)))
               (lambda (y) (+ x y 0))))
MAKE-ADDER

CL-USER> (disassemble #'make-adder)
; disassembly for MAKE-ADDER
; Size: 151 bytes. Origin: #x5344C6D5 ; MAKE-ADDER
; 6D5: 4881FF17011050 CMP RDI, #x50100117 ; NIL
; 6DC: 7449 JEQ L2
; 6DE: 4D896D28 MOV [R13+40], R13 ; thread.pseudo-atomic-bits
; 6E2: 498B4D48 MOV RCX, [R13+72] ; thread.boxed-tlab
; 6E6: 488D4120 LEA RAX, [RCX+32]
; 6EA: 493B4550 CMP RAX, [R13+80]
; 6EE: 0F873D010000 JNBE #x5344C831
; 6F4: 49894548 MOV [R13+72], RAX ; thread.boxed-tlab
; 6F8: 80C90B OR CL, 11
; 6FB: B839020000 MOV EAX, 569
; 700: 490B45E0 OR RAX, [R13-32]
; 704: 488941F5 MOV [RCX-11], RAX
; 708: 4D316D28 XOR [R13+40], R13 ; thread.pseudo-atomic-bits
; 70C: 7401 JEQ L0
; 70E: F1 ICEBP
; 70F: L0: 488D05BA000000 LEA RAX, [RIP+186] ; = #x5344C7D0
; 716: 488941FD MOV [RCX-3], RAX
; 71A: 4C895105 MOV [RCX+5], R10
; 71E: 488BD1 MOV RDX, RCX
; 721: L1: 488BE5 MOV RSP, RBP
; 724: F8 CLC
; 725: 5D POP RBP
; 726: C3 RET
; 727: L2: 4D896D28 MOV [R13+40], R13 ; thread.pseudo-atomic-bits
; 72B: 498B4D48 MOV RCX, [R13+72] ; thread.boxed-tlab
; 72F: 488D4120 LEA RAX, [RCX+32]
; 733: 493B4550 CMP RAX, [R13+80]
; 737: 0F8701010000 JNBE #x5344C83E
; 73D: 49894548 MOV [R13+72], RAX ; thread.boxed-tlab
; 741: 80C90B OR CL, 11
; 744: B839020000 MOV EAX, 569
; 749: 490B45E0 OR RAX, [R13-32]
; 74D: 488941F5 MOV [RCX-11], RAX
; 751: 4D316D28 XOR [R13+40], R13 ; thread.pseudo-atomic-bits
; 755: 7401 JEQ L3
; 757: F1 ICEBP
; 758: L3: 488D0521000000 LEA RAX, [RIP+33] ; = #x5344C780
; 75F: 488941FD MOV [RCX-3], RAX
; 763: 4C895105 MOV [RCX+5], R10
; 767: 488BD1 MOV RDX, RCX
; 76A: EBB5 JMP L1
NIL

CL-USER> (disassemble (make-adder 10))
; disassembly for (LAMBDA (Y) :IN MAKE-ADDER)
; Size: 25 bytes. Origin: #x5344C798 ; (LAMBDA (Y)
                             ...

Read more...

Revision history for this message
Stas Boukarev (stassats) wrote :

The handling of multiple functions inside a single code object is inconsistent.

Changed in sbcl:
assignee: nobody → Stas Boukarev (stassats)
Stas Boukarev (stassats)
Changed in sbcl:
status: New → Fix Committed
assignee: Stas Boukarev (stassats) → nobody
Revision history for this message
Michał "phoe" Herda (phoe-krk) wrote :

Thanks!

Revision history for this message
Andrew Berkley (ajberkley) wrote :
Changed in sbcl:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Duplicates of this bug

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.