← All issues

DFG node for `String.fromCodePoint`

93a4fbe

Source/JavaScriptCore/dfg/DFGOperations.cpp

+JSC_DEFINE_JIT_OPERATION(operationStringFromCodePointUntyped, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedValue))
+{
+ ...
+ double codePointAsDouble = value.toNumber(globalObject);
+ OPERATION_RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
+ uint32_t codePoint = static_cast<uint32_t>(codePointAsDouble);
+ if (codePoint != codePointAsDouble || codePoint > UCHAR_MAX_VALUE) [[unlikely]] {
+ throwRangeError(globalObject, scope, "Arguments contain a value that is out of range of code points"_s);
+ OPERATION_RETURN(scope, encodedJSValue());
+ }
+
+ if (U_IS_BMP(codePoint))
+ OPERATION_RETURN(scope, JSValue::encode(jsSingleCharacterString(vm, static_cast<char16_t>(codePoint))));
+
+ char16_t buffer[2] = { U16_LEAD(codePoint), U16_TRAIL(codePoint) };
+ OPERATION_RETURN(scope, JSValue::encode(jsNontrivialString(vm, String({ buffer, 2 }))));
+}

JSC's tiered JIT (Baseline → DFG → FTL) recognises specific built-in functions at the bytecode parsing stage and emits purpose-built IR nodes. String.fromCodePoint is harder than String.fromCharCode because (1) it must throw RangeError for non-integer, negative, or >0x10FFFF inputs — even when the JIT has proven the argument is an Int32 — and (2) supplementary-plane code points require emitting a two-unit surrogate pair.

This commit adds a StringFromCodePoint DFG node and FromCodePointIntrinsic. For Int32 arguments in [0, 0xFF], the fast path is shared with StringFromCharCode via a unified compileStringFromCharCodeOrCodePoint. Larger code points, surrogate pairs, and RangeError cases fall back to operationStringFromCodePoint (Int32 slow path) or operationStringFromCodePointUntyped. The node keeps NodeMustGenerate set and models itself as write(SideState) to prevent the optimiser from treating it as side-effect-free.

The change delivers a measured ~5.2x speedup for Latin-1 code points and brings String.fromCodePoint into the JIT intrinsic tier. Every new DFG intrinsic must be wired through clobberize, doesGC, safeToExecute, prediction propagation, loop unrolling, and both 32-bit and 64-bit speculative JIT backends, making each of those phase interactions a potential miscompilation surface.

🔒

New JIT code paths for a spec function with error semantics — edge cases in the fast path and runtime helpers warrant security investigation.

Subscribe to read more