Worked examples — Calling conventions and ABI
5.1.10 · D3· Hardware › Instruction Set Architecture (ISA) › Calling conventions and ABI
Hum System V AMD64 ABI (Linux/macOS x86-64) ko concrete model ke roop mein use karte hain, bilkul waise hi jaise parent ne kiya tha. Kuch bhi shuru karne se pehle, ek refresher taaki koi bhi symbol unexplained na rahe:
Scenario matrix
Har calling-convention problem inhi cells mein se ek hoti hai. Neeche ke examples ko us cell ke saath label kiya gaya hai jo woh cover karte hain.
| # | Case class | Kya mushkil hai | Example |
|---|---|---|---|
| A | Kam args, koi locals nahi | Baseline — kuch bhi spill nahi, kuch bhi saved nahi | Ex 1 |
| B | 6 se zyada int args | Args 7+ stack par spill hote hain, right-to-left | Ex 2 |
| C | Mixed int + float args | Do alag counters: GP regs aur XMM regs | Ex 3 |
| D | Callee-saved discipline | Non-volatile register ko push/pop karna zaroori |
Ex 4 |
| E | Recursion / nested frames | Har call apna khud ka frame banata hai; return chain | Ex 5 |
| F | Alignment (degenerate stack) | Ek extra push movaps tod deta hai; padding fix |
Ex 6 |
| G | Zero args, void return | Degenerate: koi arg regs nahi, rax undefined |
Ex 7 |
| H | Small struct by value | Struct ≤ 16 B do registers mein split hoti hai | Ex 8 |
| I | Word problem (real bug) | ABI mismatch se silent corruption hoti hai | Ex 9 |
| J | Exam twist | Mid-call mein kaunse register mein value hai? | Ex 10 |
Ex 1 — Cell A: baseline, sab kuch registers mein
Forecast: aage padhne se pehle teen registers guess karo.
- Args ko ordered list se match karo.
a→rdi,b→rsi,c→rdx. Yeh step kyun? Orderrdi, rsi, rdx, …convention se fixed hai taaki callee — jo kisi bhi caller ko jaane bina likhi gayi hai — hamesha ek hi boxes mein dekhe. - Return register mein compute karo.
add3pehlea+bphir+ckorax(iske 32-bit halfeax) mein daalta hai. Yeh step kyun? Caller sirfraxko integer return ke liye read karta hai; answer kahin aur daalne ka matlab hai caller garbage read karega. - Koi stack frame nahi. 3 args ≤ 6, koi locals nahi ⇒ kuch bhi spill nahi hota,
rspkabhi nahi hilda. Yeh step kyun? Memory touch karna registers se slower hai; convention is tarah design ki gayi hai ki chhoti functions kabhi nahi karti.
Verify: . Caller eax read karta hai aur 60 paata hai. ✓
Ex 2 — Cell B: saatwan argument stack par spill hota hai
Forecast: hamare paas sirf 6 int registers hain — argument #7 kahan jaega?
- Chhe registers fill karo.
a→rdi, b→rsi, c→rdx, d→rcx, e→r8, f→r9. Yeh step kyun? Isse 6 ka integer-register budget exhaust ho jaata hai. - Overflow args right-to-left push karo. Caller
g(sirf ek overflow) kocallse pehle stack par push karta hai. Yeh step kyun? Right-to-left pushing ka matlab hai leftmost overflow argument sabse chhote address par khatam hota hai — ek fixed layout jise callee index kar sakta hai. gko callee ke andar locate karo. Entry par,rspreturn address (8 bytes,calldwara push kiya gaya) par point karta hai.giske bilkul upar baith ta hai, isliye callee[rsp+8]se read karta hai. Yeh step kyun? Callee ko us return address ka hisaab lagana hoga jocallne iske aur push kiye gaye argument ke beech insert kiya.
Stack figure dekho — return address chalk-blue box hai, g pink box hai iske bilkul upar.

Verify: function g = 7 return karta hai, [rsp+8] se read karke, rax mein rakha. ✓
Ex 3 — Cell C: mixed integer aur floating-point arguments
Forecast: kya ints aur doubles ek counter share karte hain, ya do?
- Do independent counters chalao. Integers
rdi, rsi, …consume karte hain; doublesxmm0, xmm1, …consume karte hain. Yeh interleave nahi hote ek list mein. Yeh step kyun? Integer aur float values physically alag register files mein rehti hain, isliye ABI do alag cursors rakhta hai. - Assign karo.
a→rdi(1st int),x→xmm0(1st float),b→rsi(2nd int),y→xmm1(2nd float). Yeh step kyun? Har argument sirf apna counter advance karta hai. - Return. Ek
doublereturnxmm0mein wapas aata hai,raxmein nahi. Yeh step kyun? Float results bhi float register file mein travel karte hain.
Verify: counters 2 ints (rdi, rsi) aur 2 floats (xmm0, xmm1) par khatam hote hain — signature mein 2 ints + 2 doubles se match karta hai. ✓
Ex 4 — Cell D: callee-saved discipline
Forecast: rbx callee-saved hai — yeh g ko kya karne par majboor karta hai?
- Entry par save karo.
push rbxise use karne se pehle. Yeh step kyun?rbxnon-volatile hai: caller trust karta hai ki uski value call ke baad bachi rahegi. Ise use karne ke liye,gko pehle caller ki copy stack par rakhni hogi. - Kaam karo.
rbxaccumulate karta hai. Yeh step kyun? Yeh exactly hai, pehle integers ka sum. - Exit par restore karo.
rbx→raxmove karo (return), phirpop rbxcaller ko uski value wapas dene ke liye. Yeh step kyun? Promise todna caller ko silently corrupt karta hai — parent ka Example 3 warning.
Verify: x=5 ke liye, . rax mein return = 10, rbx restored. ✓
Ex 5 — Cell E: recursion, nested frames
Forecast: sabse gehre point par stack par kitne return addresses hain?
- Har call ek frame banata hai.
fac(4)callsfac(3)callsfac(2)callsfac(1). Dekho Recursion and Activation Records. Yeh step kyun? Har invocation ko apna khud kanaur apna khud ka return address chahiye; stack (neeche grow karte hue) har call ko fresh space deta hai jo collide nahi kar sakti. - Return addresses count karo. Sabse gehre point par (
fac(1)), chaarcalls execute ho chuke hain, isliye chaar return addresses stacked hain. Yeh step kyun?callexactly ek return address push karta hai har invocation per; abhi tak kuch bhi return nahi hua hai. - Unwind karo, multiply karte hue.
fac(1)=1, phir2·1=2,3·2=6,4·6=24, har resultraxke through wapas flow karta hai. Yeh step kyun? Harretapna return address pop karta hai aur apnaraxcaller ken * …tak pohoncha ta hai.
Figure dekho — har nested frame ek chalk box hai; arrows rax ko wapas climb karte dikhate hain.

Verify: , rax mein return hota hai. ✓
Ex 6 — Cell F: alignment trap (degenerate stack)
Forecast: push ki gayi bytes count karo aur alignment rule check karo.
rsp mod 16track karo.gki entry par,rsp \equiv 8 \pmod{16}(golden rule:callne 16-aligned stack par 8 bytes push kiye). Yeh step kyun? Alignment ek running invariant hai; tum ise byte by byte audit karte ho.push rbxise shift karta hai.push rbxke baad,rsp \equiv 8 - 8 = 0 \pmod{16}. Yeh step kyun? Har 8-byte pushrspse 8 subtract karta hai, low nibble ko 0 aur 8 ke beech flip karta hai.- Inner
callise tod deta hai. Ab helper call karne se ek aur 8-byte return address push hoti hai: helper enters withrsp \equiv 0 - 8 = 8 \pmod{16}— lekin helper ki apni body phirmovapschahti hai 16-byte boundaries par aurrspko 8 se off paati hai. Ek extrasub rsp, 8(ya ek doosra push)rsp \equiv 8ko uski entry par restore karta hai taaki uski aligned locals 16 ke multiples par land karein. Yeh step kyun?movapsmisalignment par hard fault karta hai; compiler frames ko exactly ek 8-byte gap se pad karta hai invariant ko maintain karne ke liye.
Verify: entry 8; ek push ke baad 0; fix sub rsp,8 deta hai 0-8 \equiv 8 \pmod{16}, required entry value restore karta hai. ✓
Ex 7 — Cell G: zero args, void return (degenerate)
Forecast: trick question — zero args ke liye kitne arg registers?
- Koi argument registers touch nahi hote. Zero args ⇒
rdi … r9mein jo bhi caller ka tha woh hi rahega. Yeh step kyun? Convention registers sirf argument ke hisaab se reserve karta hai; koi argument nahi to kuch bhi reserve nahi karta. raxundefined hai. Ekvoidreturn ka matlab caller koraxread nahi karna chahiye — isme kuch bhi ho sakta hai, aurbeepne ise clobber kiya ho sakta hai (yeh caller-saved hai). Yeh step kyun? Koi return value nahi matlabraxke baare mein koi promise nahi; yahan koi value assume karna ek classic bug hai.
Verify: contract check — args used = 0, rax unspecified. ✓ (compute karne ke liye kuch numeric nahi)
Ex 8 — Cell H: small struct passed by value
Forecast: kya 16-byte struct stack par jaati hai, ya registers mein?
- Struct classify karo. Yeh exactly 16 bytes ke integer fields hai ⇒ yeh do integer registers mein fit hoti hai. Yeh step kyun? SysV AMD64 ek chhoti (≤ 16 B) struct ko field-by-field registers mein split karta hai memory par push karne ke bajaaye — faster.
- Do eightbytes assign karo.
p.x→rdi,p.y→rsi. Yeh step kyun? Pehle 8 bytes pehle free GP register mein jaate hain, agle 8 doosre mein. - Scalar return karo.
sumPx+ykoraxmein compute karta hai. Yeh step kyun?longresultraxmein travel karta hai kisi bhi integer ki tarah.
Verify: rax mein; struct ne rdi, rsi consume kiya (2 registers = 16 bytes / 8). ✓
Ex 9 — Cell I: real-world word problem (silent ABI bug)
Forecast: agar calling convention match karti hai, to kyun toot ta hai?
- Mismatch isolate karo. Calling convention (kaun se registers, kaun kya save karta hai) identical hai — isliye linking koi error nahi dhundta. Yeh step kyun? Linkers symbol names match karte hain, data-layout agreements nahi. Dekho Name Mangling in C++ ki kaise names akele type differences chhupa sakte hain.
- Asli toot a contract naam do. Data type size/alignment ABI ka hissa hai, calling convention ka nahi. Ek side 10 bytes read karta hai jahan doosre ne 16 likhe.
Yeh step kyun? Parent ka steel-man: calling convention ⊂ ABI. Do objects ek convention share kar sakte hain phir bhi
long doublewidth par disagree kar sakte hain. - Consequence. Bytes galat read hote hain → silent numeric corruption, koi crash nahi, koi linker warning nahi. Yeh step kyun? Link time par koi bhi byte layout check nahi karta, isliye failure sirf wrong output mein surface hoti hai.
Verify: contract check — convention identical, sizeof(long double) differ karta hai (10 vs 16 bytes) ⇒ ABI mismatch. ✓
Ex 10 — Cell J: exam twist ("mid-call mein kaun sa register?")
Forecast: rcx preserve karna kiska kaam tha?
rcxclassify karo.rcxcaller-saved (volatile) hai: callee ise freely clobber kar sakta hai. Yeh step kyun? Parent ki list: caller-saved =rax, rcx, rdx, rsi, rdi, r8–r11.- Responsibility locate karo. Kyunki
rcxcaller-saved hai,A(caller) ko isecallse pehle save karna hoga agar baad mein chahiye —Bkuch nahi deta. Yeh step kyun? Split ka poora point yahi hai ki wasted saves avoid ho sakein; volatile registers caller ki problem hain. - Fix.
Akocall Bse pehlepush rcxaur baad meinpop rcxkarna chahiye — ya value ko callee-saved register (rbx, r12–r15) mein rakhna chahiye. Yeh step kyun? Dono mein se koi bhi strategy value survive kara deti hai: save-around-call, ya use jagah park karo jahan callee preserve karne ka promise karta hai.
Verify: rcx ∈ caller-saved set ⇒ ise preserve karna caller ki duty hai. A ne nahi kiya ⇒ correct diagnosis. ✓
Recall Har example kaun sa cell tha?
Ex1 A · Ex2 B · Ex3 C · Ex4 D · Ex5 E · Ex6 F · Ex7 G · Ex8 H · Ex9 I · Ex10 J.
Recall Quick self-test
7th integer argument kahan rehta hai? ::: Stack par [rsp+8] par entry par ([rsp] par return address hai).
Ek double argument aur ek int argument — kya yeh ek register counter share karte hain? ::: Nahi: ints rdi… use karte hain, floats xmm0… use karte hain, do alag counters.
void function — kya rax meaningful hai? ::: Nahi, undefined; kabhi read mat karo.
rcx ek call ke across clobber hua — kiski galti? ::: Caller ki; rcx caller-saved hai, isliye caller ko ise preserve karna hoga.
Same calling convention lekin long double size differ kare — kya toot ta hai? ::: ABI (data sizing), silent corruption karti hai; linker ise pakad nahi paega.
Dekho bhi: x86-64 vs ARM64 AAPCS ki ARM par yeh cells kaise change hoti hain, aur System Calls and Syscall Interface argument passing ke kernel-boundary variant ke liye.