Academic Company Events NI Developer Zone Support Solutions Products & Services Contact NI MyNI
LabVIEW Zone LabVIEW Tools Network Resources LabVIEW Champions Student Corner User Groups Discussion Forum Code Sharing Learning Center

Assembly Review

In case you haven't looked at assembly before, and to orient you with the basics of this assembler's syntax, the following graphic shows one instruction in annotated detail. The element to the left, the memory address, would normally be eight hex digits for a 32 bit processor, but I've shortened it for readability. The Opcode mnemonics are pretty straightforward; mov= move, jmp= jump. The ones you might need hints on are lea = load effective address (compute, but don't dereference the address), j? = conditional jump (je = jump equals, jg = jump greater, etc.), and cmp = compare. It is also very important to note that the destination is the first operand, on the left, and the source is the second, on the right -- things don't make much sense if you get that wrong. For more detail on the Intel instruction set, see Reference for Intel x86 Assembly.

 

LabVIEW and C Generated Code
The output of the disassembler is on the left. The right column contains annotations that will hopefully make the code more understandable. The blue stripe to the left of the code highlights the loop body, where most of the execution will take place. I've trimmed the addresses to three digits to make it a bit easier to read.

LabVIEW Annotations
  1DD  mov    dword ptr [ebp+2D8h],0 zero out the loop counter
  1E7  mov    esi,dword ptr [ebp+2E0h]  
  1ED  mov    eax,esi  
  1EF  cmp    eax,0  
  1F4  je     1FE check for empty array (encoded by NULL pointer)
  1FA  mov    esi,dword ptr [esi] and determine how many times the loop will iterate
  1FC  mov    eax,dword ptr [esi]  
  1FE  mov    ecx,eax  
  200  mov    dword ptr [ebp+2DCh],ecx  
  206  mov    esi,dword ptr [ebp+2E0h]  
  20C  lea    edi,[ebp+2ECh] Create temporary
  212  cmp    esi,0   pointer,
  215  je     235   stride,
  21B  mov    esi,dword ptr [esi]   count
  21D  push       eax block for each autoindexing input array
  21E  mov    eax,dword ptr [esi] and initialize it
  220  mov    dword ptr [edi+4],eax  
  223  pop    eax  
  224  mov    dword ptr [edi+8],4  
  22B  add    esi,4  
  22E  mov    dword ptr [edi],esi  
  230  jmp    249  
  235  mov    dword ptr [edi],4  
  23B  mov    dword ptr [edi+4],0  
  242  mov    dword ptr [edi+8],4  
  249  lea    esi,[ebp+2ECh]  
  24F  mov    eax,dword ptr [esi] Adjust pointers for initial increment,
  251  sub    eax,dword ptr [esi+8] and check for empty array and possibly skip loop
  254  mov    dword ptr [esi],eax  
  256  lea    edi,[ebp+2D4h]  
  25C  mov    dword ptr [edi],eax  
  25E  cmp    dword ptr [ebp+2DCh],0  
  265  jg     270  
  26B  jmp    2DD  
  270  lea    esi,[ebp+2ECh]  
  276  mov    eax,dword ptr [esi] Advance pointers used to access array elements
  278  add    eax,dword ptr [esi+8]  
  27B  mov    dword ptr [esi],eax  
  27D  lea    edi,[ebp+2D4h]  
  283  mov    dword ptr [edi],eax  
  285  mov    eax,dword ptr [ebp+2D4h]  
  28B  mov    eax,dword ptr [eax] check if array element is zero
  28D  cmp    eax,0  
  292  je     29D  
  298  jmp    2AE  
  29D  mov    eax,dword ptr [ebp+2D0h]  
  2A3  add    eax,1 Add one to value in shift register
  2A8  mov    dword ptr [ebp+2D0h],eax  
  2AE  mov    eax,dword ptr [ebp+68h] Check timer
  2B1  cmp    dword ptr [eax+18h],0 process UI events
  2B5  je     3AC (for Abort button, window moving, debugging, etc.)
  2BB  mov    eax,dword ptr [ebp+2DCh]  
  2C1  mov    ecx,dword ptr [ebp+2D8h]  
  2C7  add    ecx,1 Increment loop counter
  2CA  cmp    ecx,eax and test for last iteration
  2CC  jge    2DD  
  2D2  mov    dword ptr [ebp+2D8h],ecx  
  2D8  jmp    270 *** END of LOOP
  C Annotations
  C7A  mov    ecx,dword ptr [ebp-4] store number of array elements into local
  C7D  mov    edx,dword ptr [ecx]  
  C7F  add    edx,4  
  C82  mov    dword ptr [ebp-8],edx  
  C85  cmp    dword ptr [ebp-4],0 if(arrayBlock && (arraySize= **(int32**)arrayBlock))
  C89  je     ccc  
  C8B  mov    eax,dword ptr [ebp-4]  
  C8E  mov    ecx,dword ptr [eax]  
  C90  mov    edx,dword ptr [ecx]  
  C92  mov    dword ptr [ebp-14h],edx  
  C95  cmp    dword ptr [ebp-14h],0  
  C99  je     cc  
  C9B  mov    dword ptr [ebp-10h],0 zero out loop count
  CA2  jmp    cad ptetest loop before beginning
  CA4  mov    eax,dword ptr [ebp-10h] for(i= 0; i < arraySize; i++) *** Beginning of LOOP
  CA7  add    eax,1  
  CAA  mov    dword ptr [ebp-10h],eax  
  CAD  mov    ecx,dword ptr [ebp-10h]  
  CB0  cmp    ecx,dword ptr [ebp-14h]  
  CB3  jge    ccc  
  CB5  mov    edx,dword ptr [ebp-10h]   if(array[i] == 0)
  CB8  mov    eax,dword ptr [ebp-8]  
  CBB  cmp    dword ptr [eax+edx*4],0  
  CBF  jne    cca  
  CC1  mov    ecx,dword ptr [ebp-0Ch]    count ++;
  CC4  add    ecx,1  
  CC7  mov    dword ptr [ebp-0Ch],ecx  
  CCA  jmp    ca4 *** END of LOOP
  CCC  mov    edx,dword ptr [ebp-0Ch] printf("Located %ld zeroes in array", count);
  CCF  push   edx  
  CD0  push   offset string db8 push pointer to format string
  CD5  push   0  
  CD7  push   offset rcsid b74  
  CDC  movsx  eax,word ptr bcc  
  CE3  add    eax,0Dh  
  CE6  push   eax  
  CE7  push   offset _fileName_ (01f5eb50)  
  CEC  lea    ecx,[ebp-34h]  
  CEF  call   @ILT+106745  

Figure 5. Disassembled Code from LabVIEW and C Compilers

Summary
I know that was a lot of information, but that is why higher level languages were invented -- to encode the operations in a form more suitable to humans than CPUs. For those that are familiar with Intel assembly, you may notice that there are several areas where the LabVIEW generated code could be improved, and rest assured, work is already underway to improve register usage. Even at the current state, the body of the loop is just under twice the size of the C code, and that is roughly what is expected.

The LabVIEW environment trades some efficiency of the computer for development power of the end user. As an example, the loop above has code in it that tests a timer and periodically checks with the UI to let you abort the VI or move and interact with the window in the LabVIEW environment. Similarly, the LabVIEW array is a dynamically sized array. This means that before entering the loop certain things need to be verified. Is there an array? Is it empty? If there are multiple input arrays and an input to the N of the loop, which is smallest? These considerations add a bit to the execution of the VI, but they don't actually affect the inside of the loop, so in the larger scope as the loop count increases, these conveniences don't cost much. For reference, the typical instruction above will execute in about one or two nanoseconds assuming it is in the cache.

Details
The LabVIEW VI above was compiled as a subroutine, which removes the generated code for debugging. As a regular priority VI with debugging the code has an additional 37 instructions in the body of the for loop.

The C code was compiled in standard mode with symbols -- without optimization turned on. This was for ease in annotation and understanding. With optimization, the code of the loop body shrank to six instructions but was much more difficult to annotate because the optimizer rearranges the instructions.