|
129 | 129 | "mamba3_residual_to_m2rnn_norm_weight", |
130 | 130 | "m2rnn_residual_to_attention_norm_weight", |
131 | 131 | ) |
| 132 | +# Block A: the first brick of an in-region chain needs an eager pre-block |
| 133 | +# RMSNorm so the M-block consumes ``norm(hidden_entry)`` instead of raw |
| 134 | +# ``hidden_entry``. The acceptance fixture binds this weight via a shared |
| 135 | +# logical name so the existing M-only acceptance tests can route it as a |
| 136 | +# real ABI input alongside the inter-brick bridge weights. |
| 137 | +_MAMBA3_FP8_TRAIN_ENTRY_RMSNORM_INPUTS = ("mamba3_entry_rmsnorm_weight",) |
132 | 138 | MAMBA3_FP8_TRAIN_REQUIRED_REAL_ABI_INPUTS = ( |
| 139 | + *_MAMBA3_FP8_TRAIN_ENTRY_RMSNORM_INPUTS, |
133 | 140 | *_MAMBA3_REAL_ABI_INPUTS, |
134 | 141 | *_MAMBA3_FP8_TRAIN_RESIDUAL_RMSNORM_INPUTS, |
135 | 142 | *_M2RNN_REAL_ABI_INPUTS, |
@@ -1395,6 +1402,47 @@ def _path_c_acceptance_fixture_surfaces_from_route_symbols( |
1395 | 1402 | mamba_count = 0 |
1396 | 1403 | m2rnn_count = 0 |
1397 | 1404 | attention_count = 0 |
| 1405 | + # Block A: emit the pre-first-brick entry RMSNorm before the route |
| 1406 | + # walker consumes the first symbol. The acceptance fixture binds |
| 1407 | + # this norm weight under a shared canonical logical name so the |
| 1408 | + # existing M-only ABI / region tests continue to round-trip. |
| 1409 | + first_symbol = normalized[0] |
| 1410 | + if first_symbol == "M": |
| 1411 | + entry_norm_name = ( |
| 1412 | + "mamba3_entry_rmsnorm" |
| 1413 | + if shared_acceptance_abi |
| 1414 | + else "mamba3_scan_entry_rmsnorm" |
| 1415 | + ) |
| 1416 | + elif first_symbol == "R": |
| 1417 | + entry_norm_name = ( |
| 1418 | + "m2rnn_entry_rmsnorm" |
| 1419 | + if shared_acceptance_abi |
| 1420 | + else "m2rnn_packed_post_entry_rmsnorm" |
| 1421 | + ) |
| 1422 | + else: |
| 1423 | + entry_norm_name = ( |
| 1424 | + "attention_entry_rmsnorm" |
| 1425 | + if shared_acceptance_abi |
| 1426 | + else "attention_qkv_projection_entry_rmsnorm" |
| 1427 | + ) |
| 1428 | + entry_norm_weight = ( |
| 1429 | + f"{first_symbol.lower()}_entry_rmsnorm_weight" |
| 1430 | + # Shared acceptance ABI keeps the legacy mamba3-flavoured logical |
| 1431 | + # weight name so tests that lock the M-only acceptance contract do |
| 1432 | + # not need to know about per-symbol weight aliases. |
| 1433 | + if not shared_acceptance_abi or first_symbol != "M" |
| 1434 | + else "mamba3_entry_rmsnorm_weight" |
| 1435 | + ) |
| 1436 | + entry_normed_hidden = f"{entry_norm_name}_hidden" |
| 1437 | + surfaces.append( |
| 1438 | + _entry_rmsnorm_surface( |
| 1439 | + name=entry_norm_name, |
| 1440 | + hidden=route_hidden, |
| 1441 | + norm_weight=entry_norm_weight, |
| 1442 | + normed_hidden=entry_normed_hidden, |
| 1443 | + ) |
| 1444 | + ) |
| 1445 | + route_hidden = entry_normed_hidden |
1398 | 1446 | for index, symbol in enumerate(normalized): |
1399 | 1447 | next_symbol = normalized[index + 1] if index + 1 < len(normalized) else "" |
1400 | 1448 | if symbol == "M": |
@@ -1587,6 +1635,26 @@ def _path_c_model_surfaces_from_bricks( |
1587 | 1635 | residual_hidden=initial_hidden, |
1588 | 1636 | route_hidden=initial_hidden, |
1589 | 1637 | ) |
| 1638 | + # Block A: the eager forward applies ``layers.{first_in_region}.norm`` |
| 1639 | + # to the prefix output before the first in-region block consumes it. |
| 1640 | + # Mirror that contract by emitting an entry RMSNorm surface ahead of |
| 1641 | + # the first brick so the M-block (or R/A) reads the normalized |
| 1642 | + # hidden state. Keep ``residual_hidden`` pointing at the raw entry |
| 1643 | + # hidden state -- the bridge AFTER the first brick combines the raw |
| 1644 | + # hidden with the first brick's delta, matching the eager |
| 1645 | + # ``residual + delta`` accumulator. |
| 1646 | + first_brick = bricks[0] |
| 1647 | + entry_norm_name = f"{first_brick.name}_entry_rmsnorm" |
| 1648 | + entry_normed_hidden = f"{entry_norm_name}_hidden" |
| 1649 | + context.surfaces.append( |
| 1650 | + _entry_rmsnorm_surface( |
| 1651 | + name=entry_norm_name, |
| 1652 | + hidden=context.route_hidden, |
| 1653 | + norm_weight=f"{entry_norm_name}_weight", |
| 1654 | + normed_hidden=entry_normed_hidden, |
| 1655 | + ) |
| 1656 | + ) |
| 1657 | + context.route_hidden = entry_normed_hidden |
1590 | 1658 | for index, brick in enumerate(bricks): |
1591 | 1659 | lowerer = _path_c_model_brick_surface_lowerer_for(brick.route_symbol) |
1592 | 1660 | if lowerer is None: |
@@ -1806,6 +1874,32 @@ def _residual_norm_surface( |
1806 | 1874 | ) |
1807 | 1875 |
|
1808 | 1876 |
|
| 1877 | +def _entry_rmsnorm_surface( |
| 1878 | + *, |
| 1879 | + name: str, |
| 1880 | + hidden: str, |
| 1881 | + norm_weight: str, |
| 1882 | + normed_hidden: str, |
| 1883 | +) -> FusionKernelSurface: |
| 1884 | + """Return the pre-block entry RMSNorm surface for the first in-region brick. |
| 1885 | +
|
| 1886 | + Unlike :func:`_residual_norm_surface`, this op does not fold a residual |
| 1887 | + add: the eager forward applies ``self.norm(hidden)`` immediately before |
| 1888 | + the first in-region block runs and uses the raw ``hidden`` as the |
| 1889 | + residual baseline downstream. The fused region mirrors that by emitting |
| 1890 | + a single-input RMSNorm whose output replaces ``route_hidden`` while |
| 1891 | + ``residual_hidden`` keeps pointing at the raw entry hidden state. |
| 1892 | + """ |
| 1893 | + |
| 1894 | + return FusionKernelSurface.path_c( |
| 1895 | + name=name, |
| 1896 | + op_name="entry_rmsnorm", |
| 1897 | + inputs=(hidden, norm_weight), |
| 1898 | + outputs=(normed_hidden,), |
| 1899 | + backward="aot_autograd", |
| 1900 | + ) |
| 1901 | + |
| 1902 | + |
1809 | 1903 | def _prefixed_abi_inputs( |
1810 | 1904 | prefix: str, |
1811 | 1905 | names: Sequence[str], |
|
0 commit comments