Hi,
`raw_assembler()->IntPtrAddWithOverflow(a, b)` goes to `INTPTR_BINOP(Int,
AddWithOverflow)` in raw-machine-assembler.h line 639, which expands to:
```
Node* IntPtrAddWithOverflow(Node* a, Node* b) {
return kSystemPointerSize == 8 ? Int64AddWithOverflow(a, b)
: Int32AddWithOverflow(a, b);
}
```
I'm guessing that you have a 64-bit machine, so this will insert
a Int64AddWithOverflow node in the graph. During instruction selection,
intruction-selector.cc will call VisitInt64AddWithOverflow, which depending
on your architecture will emit a kX64Add (x64) or a kArm64Add (arm64)
operation, dealing with the overflow through a ForSet FlagsContinuation.
And then, the code generator will eventually emit a `addl` (on x64) or a
`adds` (on arm64), which will set the overflow flags appropriately,
combined an instruction to materialize the value of the flag and store it
somewhere (something like `setb` on x64 and `cset hs` on arm64).
Well, the instruction selector will actually try to avoid materializing the
overflow bit by doing the addition right before the following branch so
that it can use the overflow flag as branch condition (with a `jo` on x64
and something like a `b.cs` on arm64). You can see how this is done by
looking at `InstructionSelector::VisitBranch` and in particular
`VisitWordCompareZero`.
Hope that helps,
Darius
On Tuesday, September 2, 2025 at 9:12:47 AM UTC+2 [email protected] wrote:
> Hi everyone,
>
> I want to study the implementation details of V8’s CodeStubAssembler
> (CSA). So I built the simplest code sample as follows:
>
> ``` js
> function add(a, b) {
> return a + b;
> }
>
> %PrepareFunctionForOptimization(add);
>
> add(0x4141, 0x4000);
> add(0x4242, 0x4001);
> ```
>
> By reading the source code, I traced this down to
> Generate_AddWithFeedback, which gets embedded into AddHandler. Since the
> above values are added as Smi, it eventually calls the TrySmiAdd function:
>
> ``` c++
> Comment("perform smi operation");
> // If rhs is known to be an Smi we want to fast path Smi operation. This
> // is for AddSmi operation. For the normal Add operation, we want to fast
> // path both Smi and Number operations, so this path should not be marked
> // as Deferred.
> TNode<Smi> rhs_smi = CAST(rhs);
> Label if_overflow(this,
> rhs_known_smi ? Label::kDeferred : Label::kNonDeferred);
> TNode<Smi> smi_result = TrySmiAdd(lhs_smi, rhs_smi, &if_overflow); // [+]
> @a
> ```
>
> But the implementation of TrySmiAdd really confuses me:
>
> ``` c++
> if (SmiValuesAre32Bits()) { // [+] hit here since
> v8_enable_pointer_compression = false
> return BitcastWordToTaggedSigned(
> TryIntPtrAdd(BitcastTaggedToWordForTagAndSmiBits(lhs),
> BitcastTaggedToWordForTagAndSmiBits(rhs), if_overflow));
> }
> ```
>
> For easier debugging and clearer memory layout, I disabled pointer
> compression (v8_enable_pointer_compression = false).
>
> This means it eventually calls the TryIntPtrAdd function, and my current
> confusion is mostly around that function.
>
> ## TryIntPtrAdd
>
> ```
> TNode<IntPtrT> CodeStubAssembler::TryIntPtrAdd(TNode<IntPtrT> a,
> TNode<IntPtrT> b,
> Label* if_overflow) {
> TNode<PairT<IntPtrT, BoolT>> pair = IntPtrAddWithOverflow(a, b); // [+] @b
> [...]
> }
> ```
>
> My main question is about `@b`: how exactly is IntPtrAddWithOverflow
> implemented?
>
> By reading the source, I found two pieces of code related to its
> definition:
>
> ``` c++
> V(IntPtrAddWithOverflow, PAIR_TYPE(IntPtrT, BoolT), IntPtrT, IntPtrT) \
>
> // Basic arithmetic operations.
> #define DECLARE_CODE_ASSEMBLER_BINARY_OP(name, ResType, Arg1Type,
> Arg2Type) \
> TNode<ResType> name(TNode<Arg1Type> a, TNode<Arg2Type> b);
> CODE_ASSEMBLER_BINARY_OP_LIST(DECLARE_CODE_ASSEMBLER_BINARY_OP)
> #undef DECLARE_CODE_ASSEMBLER_BINARY_OP
>
> #define DEFINE_CODE_ASSEMBLER_BINARY_OP(name, ResType, Arg1Type, Arg2Type)
> \
> TNode<ResType> CodeAssembler::name(TNode<Arg1Type> a, TNode<Arg2Type> b) {
> \
> return UncheckedCast<ResType>(raw_assembler()->name(a, b)); \
> }
> CODE_ASSEMBLER_BINARY_OP_LIST(DEFINE_CODE_ASSEMBLER_BINARY_OP)
> #undef DEFINE_CODE_ASSEMBLER_BINARY_OP
>
> ```
>
> Which after macro expansion should give us something like:
>
> ``` c++
> TNode<ResType> CodeAssembler::IntPtrAddWithOverflow(TNode<IntPtrT> a,
> TNode<IntPtrT> b) { \
> return UncheckedCast<PAIR_TYPE(IntPtrT,
> BoolT)>(raw_assembler()->IntPtrAddWithOverflow(a, b)); \
> }
>
> ```
>
> At this point I have two questions I cannot figure out:
>
> 1. What exactly does raw_assembler() return? I suspect it’s
> platform-related. where is its code
>
> 2. Where is the implementation of
> raw_assembler()->IntPtrAddWithOverflow(a, b) located? I tried searching the
> V8 codebase but couldn’t find it.
>
> If anyone could clarify this, I’d really appreciate it. Thanks!
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups
"v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/v8-dev/2a593be2-53d9-446b-b469-9596a7ade330n%40googlegroups.com.