0

Wondering how you accomplish this sort of thing in assembly (nasm x86 or anything).

if (a && (b || !c) && (d || e)) {
  something()
}

I am only beginning to learn about assembly so wanted to see how you would handle this "multiple variable" situation. You can't do (getting the jnz from here):

cmp eax, a && (b || !c) && (d || e)
jnz something

So it seems you would have to accumulate the value somehow.

Lance
  • 75,200
  • 93
  • 289
  • 503
  • you could manually use the Shunting-yard algorithm to solve such expressions to some rpn, which can be easily written as instructions. but keep in mind the difference between arithmetical and logical `and`/`or`/`xor`/etc. (see answer by Peter Cordes). – sivizius Jul 01 '18 at 13:17

1 Answers1

3

Every asm instruction in your source maps to at 1 machine instruction1. Your assembler is not a compiler.

What you can do in one instruction depends completely on what the machine code can do; asm source syntax can express everything. The x86 cmp instruction can only read 2 input operands and write flags, so of course it can't set flags based on 6 inputs.


Sometimes you can efficiently turn a C source boolean expression into one asm branch, e.g. logical || can use a bitwise OR, which sets ZF based on the result being non-zero. So if(a || b || c) could compile to

    or eax, ebx
    or eax, ecx
    jnz  .not_if

      ; if body
.not_if:

But more typically it's best to actually compare-and-branch on each part of the condition separately. e.g. if (a && b) might compile to:

    test   eax,eax
    jz   .not_if
    test   ebx,ebx
    jz   .not_if

         ; if body    
.not_if:

You can't test eax,ebx because bitwise AND can be 0 with EAX=1 and EBX=2, for example.

Another case you can combine things into one cmp/jcc is range-checks: double condition checking in assembly. sub to bring one end of the range to 0, then cmp / ja (unsigned compare). Jumps if the sub wrapped to a large unsigned value, or if the value was out of range high.

More generally, look at what compilers do when compiling C. Assignment to a volatile variable is a convenient thing to put in an if() body if you don't want the compiler to be able to turn the if into a branchless CMOV or something. See How to remove "noise" from GCC/clang assembly output? for more about looking at compiler output and constructing useful compiler input. Especially Matt Godbolt's CppCon talk.


Actually creating booleans in integer registers and ANDing / ORing them together works on MIPS (which doesn't have a FLAGS register, so you either branch or compare into an integer register).

Or on POWER / PowerPC where there are multiple condition-code fields and you can compare into CR0, compare into CR1, then use a condition-register instruction to combine the conditions.

IDK if you forgot to tag x86, or if you were trying to ask a generic question about different assembly languages.


Footnote 1: Many RISC architectures have "pseudo-instructions" that expand to 2 or 3 actual machine instructions, like li $t0, 0x1234567 on MIPS -> lui to set up the upper half of the 32-bit constant, and ori $t0, $t0, 0x4567 to set the low half. Fixed-instruction-width architectures can't fit an arbitrary 32-bit constant into one instruction the way x86 can.

x86 doesn't use "pseudo-instructions". They're already complex enough. :P

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847