Understand CPU Branch Instructions Better

77 pointsposted 7 months ago
by mfiguiere

7 Comments

vincent-manis

7 months ago

This is why the old-fashioned university course on assembly language is still useful. Writing assembly language (preferably for a less-complex architecture, so the student doesn't get bogged down on minutiae) gives one a gut feeling for how machines work. Running the program on a simulator that optionally pays attention to pipeline and cache misses can help a person understand these issues.

It doesn't matter what architecture one studies, or even a hypothetical one. The last significant application I wrote in assembler was for System/370, some 40 years ago. Yet CPU ISAs of today are not really that different, conceptually.

o11c

7 months ago

Decent intro, though nothing new.

A couple useful points it lacks:

* `switch` statements can be lowered in two different ways: using a jump table (an indirect branch, only possible when values are adjacent; requires a highly-predictable branch to check the range first), or using a binary search (multiple direct branches). Compilers have heuristics to determine which should be used, but I haven't played with them.

* You may be able to turn an indirect branch into a direct branch using code like the following:

  if (function_pointer == expected_function)
    expected_function();
  else
    (*function_pointer)();
* It's generally easy to turn tail recursion into a loop, but it takes effort to design your code to make that possible in the first place. The usual Fibonacci example is a good basic intro; tree-walking is a good piece of homework.

* `cmov` can be harmful (since it has to compute both sides) if branch is even moderately predictable and/or if the less-likely side has too many instructions. That said, from my tests, compilers are still too hesitant to use `cmov` even for cases where yes I really know dammit. OoO CPUs are weird to reason about but I've found that due to dependencies between other instructions, there's often some execution ports to spare for the other side of the branch.

noone_youknow

7 months ago

Nice article! Always good to see easy-to-follow explainers on these kinds of concepts!

One minor nit, for the “odd corner case that likely never exists in real code” of taken branches to the next instruction, I can think of at least one example where this is often used: far jumps to the next instruction with a different segment on x86[_64] that are used to reload CS (e.g. on a mode switch).

Aware that’s a very specific case, but it’s one that very much does exist in real code.

HelloNurse

7 months ago

> A function always has a single entry point in a program (at least, I don’t know of any exceptions to this rule)

We can consider distinct entry points as distinct functions, but it doesn't mean that different functions cannot overlap, sharing code in general and return statements. Feasibility depends on calling conventions, which are outside the topic of the article.

djmips

7 months ago

Weird cookie policy on that blog?

a_void_sky

7 months ago

its such a fascinating thing that most people just ignore i too wrote (using AI) an article on Branch Prediction after i found out that most of my team members only read this in college but never understood