Consequences of passing too few register parameters to a C function
38 points by aragonite 3 days ago | 19 comments

bananamogul 4 hours ago
Raymend Chen has probably forgotten more about programming than I'll ever know, but aren't the first two blah() function examples either missing a } or have a superfluous { after the else?
reply
billforsternz 2 hours ago
Yes. And in the second one he has return c; when he meant return b;

Homer nods.

reply
Onavo 3 hours ago
Post COVID software engineer grads probably won't understand this comment.
reply
camkego 2 hours ago
Why? Because of LLM vibe coding?
reply
dataflow 31 minutes ago
Yeah. The next generation of software engineers is coming. Brace yourself.
reply
burner420042 2 hours ago
Instantly finding a missing semicolon or unbalanced parentheses on a screen of text.

Kids these days!

reply
CodeArtisan 2 hours ago
Until C23, you could declare a pointer to a procedure that takes an unspecified amount of any type arguments like this

    void foo( int (*f)() )
    {
        f(1);
        f(1, "2" , 3.0);
    }
https://godbolt.org/z/s6e5rnqv9

If you compile with -std=c23, both gcc and clang will throw an error ( (*f)() is now the same as (*f)(void) )

reply
_kst_ 2 hours ago
It's not even possible to pass too few arguments to a function in C unless you go out of your way to write bad code.

You can write a function declaration that's inconsistent with its definition in another translation unit. Declaring the function in a shared header file avoids this.

You can use an old-style declaration that doesn't specify what parameters a function expects. Don't do that. Use prototypes.

You can use a cast to convert a function pointer to an incompatible type, and call through the resulting pointer. Don't do that.

You can call a function with no visible declaration if your compiler overly permissive or is operating in pre-C99 mode. Don't do that.

reply
userbinator 2 hours ago
This is a site for intellectual curiosity, not pedantic dissmisal.
reply
themafia 2 hours ago
You could also use inline assembly.
reply
charleslmunger 2 hours ago
I had fun exploiting this to detect the falling convention used by some code at runtime - there were two different options depending on OS version; one passed a jnienv* as the first param, the other did not. So if I called it with 0, I could tell which was being used based on whether the first argument was NULL or not. Only used for specific architectures with a defined ABI that behaved this way, of course.
reply
rurban 2 hours ago
Of which decade is this post? I cannot think of any modern architecture which still passes args on the stack.

Itanium? Stone age

reply
jcranmer 58 minutes ago
If you have 29 arguments, I assure that you some of them are on the stack in nearly every architecture in use. Also, certain types as parameters also get passed on the stack (usually types larger than a register, or in C++ code, objects with nontrivial constructors or destructors).
reply
anitil 4 hours ago
I had never considered the idea of passing too few register params so I didn't immediately think of the reuse problem. And I had no idea about Itanium's Not-a-thing bit! Always a good read from Raymond Chen.
reply
LelouBil 2 hours ago
Interesting that some CPUs have a calling convention "built-in"
reply
hyperhello 3 hours ago
Do you really not ‘pass’ register parameters? How can anyone tell if you didn’t?
reply
Polizeiposaune 2 hours ago
Read the post - not all architectures behave the same!

Itanic had variable-sized register windows, plus extra tag bits for NaT ("not a thing") placeholder values. If you didn't set one of the argument registers the callee might trap in unexpected ways when it touches the register garbage.

reply
hyperhello 33 minutes ago
Heh, it had rotating register files too. VLIW was so weird.
reply
9fwfj9r 2 hours ago
I regard this yet another unintuitive Itanium quirk that makes it failed.
reply
marlburrow 2 hours ago
[flagged]
reply