Remove the _ReadWriteBarrier declaration, the #pragma intrinsic and the
barrier call that UEF-8 introduced, and recover January's schedule from
ordinary C89.
The barrier did exactly one thing: it stopped VC7's list scheduler from
hoisting the output-pointer formation above tag_get. Two source controls
replace it.
First, index the arrays instead of walking hand-written pointers. The
sibling object_export_function_values in source/objects/objects.c is
strict-exact and indexes; January's object shows VC7 strength-reducing
that into the same walking pointers and homed down-counter, with the
cdecl cleanup immediately after the call and both bases formed by lea
and no copy. Writing the unit exporter the same way makes the whole
preamble byte-exact and takes the residual from 30 bytes to 9. The
walking pointers in the target are compiler output, not source, which is
why every pointer-form experiment in the UEF matrix plateaued.
Second, bind animation->private_loop_frame_index to a named local used
twice, in the predicate and in the division. The last nine bytes were a
load-order swap; source operand order does not control it, but a two-use
local does. UEF-12 had tested named locals and seen them eliminated, but
it bound both operands with a single use each, and a single-use copy is
propagated away.
_unit_export_function_values is 384/384 bytes, 18/18 relocations, and its
normalized SHA-256 equals the target's 4149e4cd...b20c35;
section_infos_equal is True. The emitted section is raw byte-for-byte
identical to the previously accepted barrier build.
Verified: clean forced rebuild reproduces; compare_object_exact_sets
reports 189 exact both ways with lost_exact empty; verdicts diff over all
8246 functions gains 0 and regresses 0; halobetacache_build,
libcmt_build, semantic_progress and progress pass; object-admission,
semantic-match and parked audits pass; pytest tools 203 passed;
git diff --check clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Split the camo regrowth guard's three-conjunct condition into nested ifs
with explicit else arms assigning the default rate, and scope the set
flag to the branch that uses it. Behaviour-identical: game_engine_running
is still evaluated exactly once, the short-circuit order is unchanged,
and every path that previously left set FALSE now assigns 1.f/120.f.
That flips VC7 from eager to lazy placement of the partially redundant
[ebp+8] reload. January duplicates it per predecessor -- 0x49c on the
main path, 0x4f6 at the bypass merge, 0x533 in the else arm, with the
jne going to 0x4f9 to skip the merge copy -- where we had hoisted a
single dominating copy to 0x48a.
_unit_update is now 1642 instructions with normalized_sha256 EQUAL to
January: zero differing bytes, from 632.
section_infos_equal still reports False on 2 of 259 relocations, both
the same function-local static spelled two ways: build/split has
`unit_update'::`114'::last_time where CL emits
?last_time@?HC@??unit_update@@9@9. That is the known naming gap in
config/symbols.json, not a codegen difference -- all 259 addresses and
types match and the other 69 differing records are forgiven by
symbolic_target.
CL /Zs /W3 unchanged at 43 diagnostics.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
units.c:5569 and 5582 tested and cleared animation flags by raw bit
number, which the campaign's own source rules forbid. The 2011 HCEX PDB
resolves both enumerators: _unit_animation_fallen_on_front_bit is 3 and
_unit_animation_ignore_translation_bit is 2, matching our units.h and
the emitted masks (shr cl,3 and and byte ptr [ebx+0x248],0FBh).
The names also make the resurrect ternary read correctly: fallen on the
front resurrects front, otherwise back.
Verified byte-neutral: census stays 188/1/0 code, data unchanged, and
_unit_update is bit-identical. CL /Zs /W3 unchanged at 43 diagnostics.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The 2011 HCEX PDB compiland dump records this helper as
unit_animation_update(long, long, struct animation_state *) -- the
animation pointer is the THIRD parameter. Our reconstruction had it
first. Reordering the prototype, the definition and all four call sites
to (long unit_index, long animation_graph_index, struct animation_state
*animation) also puts unit_index first, matching the convention used
everywhere else in this file.
That was the whole residual. VC7 assigns its custom register convention
by parameter order, so the wrong order changed which argument landed in
which register at every code_0019b160 call site -- the 23 differing
bytes all began at 0x195, inside the first of those argument setups.
The "one missing scratch allocation" reading of the residual was a
symptom, not the cause.
units.obj code census 187/2/0 -> 188/1/0; data unchanged.
_unit_update_animation and _code_0019b160 are both byte-exact.
CL /Zs /W3 unchanged at 43 diagnostics with identical codes.
Only _unit_update remains.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The 2011 HCEX PDB records unit_update_animation's second parameter as
struct unit_animation_update_data * -- non-const. Drop the const from
both the prototype and the definition.
Adopt January's local names from the same source: old_world_matrix and
exit_velocity in unit_update_animation, last_aiming_vector and
object_matrix in unit_update.
Also apply the layout implied by January's own assert line numbers. The
two match_assert calls in unit_update_animation carry lines 0xb61 = 2913
and 0xb73 = 2931, so seventeen lines hold the whole if, declaration and
seven-arm switch. That fits only with the declaration carrying its
initialiser and no default arm, so write
short desired_base_seat_index = NONE; and drop default: break;.
Verified byte-neutral: census stays 187/2/0 code and unchanged data;
unit_update_animation stays at 23 differing bytes and unit_update at its
one instruction. CL /Zs /W3 unchanged at 43 diagnostics.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Route the three animation_choose_random_permutation_internal call sites
through a long animation_graph_index local, matching the byte-exact
unit_start_animation_impulse idiom at units.c:8535 and units.c:3245.
unit_animation_set_state was the file's only site that inlined the field
expression into the call.
That flips one allocator decision: January spends EDI on the unit_seat
pointer (mov edi,eax / lea eax,[edi+0x58] / mov [ebp-0x1c],edi, then
direct mov ecx,[edi+0x40] and [edi+0x44] later) and homes animation_index
at [ebp-0xc); we did the reverse and reloaded the pointer at every use.
The frame going 0x18 -> 0x1c and all three or edx,0ffffffffh sites were
consequences, not separate differences.
units.obj code census 186/3/0 -> 187/2/0; data unchanged at 129/0/0.
Also reproduce January's setcc spelling in unit_update_animation: split
the shared alert/suspicious case arm and write the in_combat arm with no
byte-typed intermediate. setcc r8 ; movzx r16,r8 occurs zero times in all
9352 January functions; the new shape emits January's
xor ebx,ebx / test cl,cl / setne bl / add ebx,2 and matches January's
relocation layout exactly (relocation_infos_equal was False, now True).
Still non-exact: 23 bytes, all register choices.
CL /Zs /W3 unchanged at 43 diagnostics with identical codes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zero owners changed: the emitted object is byte-identical, verified by
function body hash and by a whole-object comparison across all 415
owners and 447 sections. Census stays 186/3/0, /Zs /W3 stays at 43
warnings. This is fidelity only.
DIA2Dump on the June-2011 HCEX.pdb reports, for unit_animation_set_state:
`Type: short, interpolation_frame_count` and `static function: true`.
Both are adopted:
- interpolation_frame_count becomes short, code_00198230 goes back to
returning short with `short result = 6;`, and the now-redundant
(short) cast at object_start_interpolation is dropped. This reverts
the "code_00198230 returns long" reconstruction landed in f38ea306,
which was only ever justified by removing one `movsx eax,ax`; the
short spelling removes it too and matches the PDB, so the long
spelling has no remaining evidence behind it.
- unit_animation_set_state becomes static. Nothing outside units.c
references it - it appears in no header and in no other translation
unit - so this cannot break a link.
Both changes were measured byte-identical individually and together
before landing, by SHA1 of the reloc-zeroed function bodies rather than
by equal counters.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replaces the previous commit's `result = code_001981f0(...)` spelling
with the one January actually used. All SIX of the function's target
idioms now match: `xor dl,dl`, `mov dl,1`, `test dl,dl`, the homed
`mov byte ptr [ebp-2],1`, its `mov al,[ebp-2]` reload at the return, and
the `xor al,al` early epilogue. The head 0x83-0x99 is now
instruction-for-instruction identical to January including register
choices.
- result = code_001981f0(new_state);
- if (!result) { return result; }
+ if (!code_001981f0(new_state))
+ {
+ result = FALSE;
+
+ goto done;
+ }
...
+done:
return result;
`goto <label>;` is house style here - eleven other files in source/ use
it. Behaviour is unchanged: the only path to the label sets result FALSE
first, so `return result` returns FALSE exactly as `return FALSE` did.
Why it works where every earlier attempt failed: VC7 constant-folds a
definition that is consumed by an immediately following `return`, which
is why `result = FALSE; return result;` has always been byte-identical to
base. A `goto` to a label in front of the common return survives
allocation, so `result` genuinely has two reaching definitions and is
homed; VC7 then tail-duplicates that exit into `xor al,al`, reproducing
January's 0x2de epilogue.
landed before raw 232/232 reg+slot 63/34 blocks 51 -> no result home
landed now raw 210/194 reg+slot 68/31 blocks result homed
reg+slot reads three bytes worse, which is normalisation noise; raw diff
bytes, block counts and the idiom set all improve, and the structural
match is what counts.
The size now reads 1392 against 1408. That is honest rather than a
regression: real code is 1195 versus January's 1203, and the previous
1408 was a 16-byte COMDAT alignment artifact. A separate drift analysis
confirms there is no block where the target has code we lack - 366
instructions against 365 - so the whole remainder is one allocator
decision: January homes a seventh carrier, the inner short
animation_index, sharing [ebp-0xc] with interpolation_frame_count, giving
sub esp,0x1c where we keep it in di and get 0x18.
Census unchanged at 186/3/0; exactly one owner changed; /Zs /W3 identical
at 43 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>