Skip to content

Commit 41af622

Browse files
committed
feat(core): add contextual/strided intra-layer checkpoint digest
1 parent fcca48b commit 41af622

3 files changed

Lines changed: 885 additions & 4 deletions

File tree

srcs/core/checkpoint_digest.c

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "lis/checkpoint_digest.h"
2+
#include "lis/intra_layer_trace.h"
23

4+
#include <float.h>
35
#include <stdint.h>
46
#include <string.h>
57

@@ -204,6 +206,354 @@ static void lis_checkpoint_digest_update_tag(lis_sha256_context *context,
204206
lis_sha256_update(context, (const unsigned char *)tag, length);
205207
}
206208

209+
#if FLT_RADIX == 2 && FLT_MANT_DIG == 24 && FLT_MAX_EXP == 128
210+
#define LIS_HAS_IEEE754_BINARY32_FLOAT 1
211+
#else
212+
#define LIS_HAS_IEEE754_BINARY32_FLOAT 0
213+
#endif
214+
215+
static int lis_checkpoint_digest_add_u64(uint64_t *total, uint64_t amount)
216+
{
217+
if (*total > UINT64_MAX - amount) {
218+
return 0;
219+
}
220+
*total += amount;
221+
return 1;
222+
}
223+
224+
static int lis_checkpoint_digest_mul_u64(uint64_t left, uint64_t right,
225+
uint64_t *product)
226+
{
227+
if (left != 0U && right > UINT64_MAX / left) {
228+
return 0;
229+
}
230+
*product = left * right;
231+
return 1;
232+
}
233+
234+
/*
235+
* The record owns a fixed-size C string. Validate both its termination and its
236+
* UTF-8 byte grammar before using strlen-style operations in the digest path.
237+
*/
238+
static lis_status lis_checkpoint_digest_validate_utf8_identifier(
239+
const char *text, size_t capacity, size_t *out_length)
240+
{
241+
size_t length = 0U;
242+
size_t index = 0U;
243+
244+
if (text == NULL || capacity == 0U || out_length == NULL) {
245+
return LIS_STATUS_INVALID_ARGUMENT;
246+
}
247+
while (length < capacity && text[length] != '\0') {
248+
++length;
249+
}
250+
if (length == 0U) {
251+
return LIS_STATUS_INVALID_ARGUMENT;
252+
}
253+
if (length == capacity) {
254+
return LIS_STATUS_FORMAT;
255+
}
256+
257+
while (index < length) {
258+
const unsigned char lead = (unsigned char)text[index];
259+
size_t continuation_count;
260+
size_t continuation_index;
261+
262+
if (lead < 0x80U) {
263+
if (lead < 0x20U || lead == 0x7fU) {
264+
return LIS_STATUS_INVALID_ARGUMENT;
265+
}
266+
++index;
267+
continue;
268+
}
269+
if (lead >= 0xc2U && lead <= 0xdfU) {
270+
continuation_count = 1U;
271+
} else if (lead >= 0xe0U && lead <= 0xefU) {
272+
continuation_count = 2U;
273+
} else if (lead >= 0xf0U && lead <= 0xf4U) {
274+
continuation_count = 3U;
275+
} else {
276+
return LIS_STATUS_FORMAT;
277+
}
278+
if (continuation_count > length - index - 1U) {
279+
return LIS_STATUS_FORMAT;
280+
}
281+
for (continuation_index = 1U;
282+
continuation_index <= continuation_count;
283+
++continuation_index) {
284+
const unsigned char byte =
285+
(unsigned char)text[index + continuation_index];
286+
287+
if (byte < 0x80U || byte > 0xbfU) {
288+
return LIS_STATUS_FORMAT;
289+
}
290+
}
291+
if ((lead == 0xe0U &&
292+
(unsigned char)text[index + 1U] < 0xa0U) ||
293+
(lead == 0xedU &&
294+
(unsigned char)text[index + 1U] > 0x9fU) ||
295+
(lead == 0xf0U &&
296+
(unsigned char)text[index + 1U] < 0x90U) ||
297+
(lead == 0xf4U &&
298+
(unsigned char)text[index + 1U] > 0x8fU)) {
299+
return LIS_STATUS_FORMAT;
300+
}
301+
index += continuation_count + 1U;
302+
}
303+
*out_length = length;
304+
return LIS_STATUS_OK;
305+
}
306+
307+
static lis_status lis_intra_layer_digest_validate_inputs(
308+
const lis_intra_layer_trace_record *record,
309+
const lis_intra_layer_observation *observation,
310+
const lis_intra_layer_fp32_view *view,
311+
const lis_intra_layer_stage_info **out_stage_info,
312+
size_t *out_precision_length)
313+
{
314+
const lis_intra_layer_stage_info *stage_info;
315+
lis_status status;
316+
uint64_t shape_product = UINT64_C(1);
317+
size_t index;
318+
319+
if (record == NULL || observation == NULL || view == NULL ||
320+
out_stage_info == NULL || out_precision_length == NULL) {
321+
return LIS_STATUS_INVALID_ARGUMENT;
322+
}
323+
if (!LIS_HAS_IEEE754_BINARY32_FLOAT || sizeof(float) != 4U) {
324+
return LIS_STATUS_UNSUPPORTED_DTYPE;
325+
}
326+
if (record->state != LIS_INTRA_LAYER_RECORD_ACTIVE) {
327+
return LIS_STATUS_BAD_STATE;
328+
}
329+
if (record->runtime_checkpoint_step == 0U ||
330+
record->total_layer_count == 0U ||
331+
record->target_layer >= record->total_layer_count) {
332+
return LIS_STATUS_INVALID_ARGUMENT;
333+
}
334+
status = lis_checkpoint_digest_validate_utf8_identifier(
335+
record->precision_path, sizeof(record->precision_path),
336+
out_precision_length);
337+
if (status != LIS_STATUS_OK) {
338+
return status;
339+
}
340+
341+
stage_info = lis_intra_layer_stage_lookup((size_t)observation->stage);
342+
if (stage_info == NULL) {
343+
return LIS_STATUS_INVALID_ARGUMENT;
344+
}
345+
if (observation->phase != LIS_INTRA_LAYER_PHASE_DECODE) {
346+
return LIS_STATUS_UNSUPPORTED;
347+
}
348+
if (observation->runtime_checkpoint_step !=
349+
record->runtime_checkpoint_step ||
350+
observation->layer_index != record->target_layer ||
351+
observation->token_position != record->token_position ||
352+
observation->batch_index != 0U ||
353+
observation->sequence_index != 0U ||
354+
observation->stage_order != stage_info->stage_order ||
355+
observation->execution_ordinal != observation->stage_order) {
356+
return LIS_STATUS_INVALID_ARGUMENT;
357+
}
358+
if (observation->rank == 0U ||
359+
observation->rank > LIS_INTRA_LAYER_MAX_RANK) {
360+
return LIS_STATUS_UNSUPPORTED_SHAPE;
361+
}
362+
for (index = 0U; index < observation->rank; ++index) {
363+
uint64_t dimension = (uint64_t)observation->shape[index];
364+
365+
if (dimension == 0U) {
366+
return LIS_STATUS_UNSUPPORTED_SHAPE;
367+
}
368+
if (!lis_checkpoint_digest_mul_u64(shape_product, dimension,
369+
&shape_product)) {
370+
return LIS_STATUS_OVERFLOW;
371+
}
372+
}
373+
if (observation->element_count == 0U ||
374+
shape_product != (uint64_t)observation->element_count) {
375+
return LIS_STATUS_SHAPE_MISMATCH;
376+
}
377+
378+
status = lis_intra_layer_fp32_view_validate(view);
379+
if (status != LIS_STATUS_OK) {
380+
return status;
381+
}
382+
if (view->rank != observation->rank ||
383+
view->logical_element_count != observation->element_count) {
384+
return LIS_STATUS_SHAPE_MISMATCH;
385+
}
386+
for (index = 0U; index < observation->rank; ++index) {
387+
if (view->shape[index] != observation->shape[index]) {
388+
return LIS_STATUS_SHAPE_MISMATCH;
389+
}
390+
}
391+
*out_stage_info = stage_info;
392+
return LIS_STATUS_OK;
393+
}
394+
395+
static lis_status lis_intra_layer_digest_preflight_stream_size(
396+
const lis_intra_layer_observation *observation,
397+
const lis_intra_layer_stage_info *stage_info,
398+
size_t precision_length)
399+
{
400+
static const char *const framed_constants[] = {
401+
LIS_INTRA_LAYER_DIGEST_VERSION,
402+
LIS_INTRA_LAYER_LAYOUT_NAME,
403+
LIS_INTRA_LAYER_STAGE_TAXONOMY,
404+
LIS_INTRA_LAYER_MODEL_FAMILY,
405+
LIS_INTRA_LAYER_PHASE_DECODE_NAME,
406+
LIS_CHECKPOINT_DIGEST_OBSERVED_DTYPE,
407+
LIS_CHECKPOINT_DIGEST_BYTE_ORDER
408+
};
409+
uint64_t total = (uint64_t)strlen(LIS_INTRA_LAYER_DIGEST_DOMAIN_TAG) + 1U;
410+
uint64_t tensor_bytes;
411+
size_t index;
412+
413+
for (index = 0U;
414+
index < sizeof(framed_constants) / sizeof(framed_constants[0]);
415+
++index) {
416+
if (!lis_checkpoint_digest_add_u64(
417+
&total, UINT64_C(8) +
418+
(uint64_t)strlen(framed_constants[index]))) {
419+
return LIS_STATUS_OVERFLOW;
420+
}
421+
}
422+
if (!lis_checkpoint_digest_add_u64(
423+
&total, UINT64_C(8) + (uint64_t)precision_length) ||
424+
!lis_checkpoint_digest_add_u64(
425+
&total, UINT64_C(8) + (uint64_t)strlen(stage_info->stage_id)) ||
426+
!lis_checkpoint_digest_add_u64(
427+
&total,
428+
UINT64_C(8) + (uint64_t)strlen(stage_info->tensor_role)) ||
429+
!lis_checkpoint_digest_add_u64(
430+
&total,
431+
(UINT64_C(10) + (uint64_t)observation->rank) * UINT64_C(8)) ||
432+
!lis_checkpoint_digest_mul_u64(
433+
(uint64_t)observation->element_count, UINT64_C(4),
434+
&tensor_bytes) ||
435+
!lis_checkpoint_digest_add_u64(&total, tensor_bytes)) {
436+
return LIS_STATUS_OVERFLOW;
437+
}
438+
if (total > UINT64_MAX / UINT64_C(8)) {
439+
return LIS_STATUS_OVERFLOW;
440+
}
441+
return LIS_STATUS_OK;
442+
}
443+
444+
lis_status lis_intra_layer_checkpoint_digest_fp32(
445+
const lis_intra_layer_trace_record *record,
446+
const lis_intra_layer_observation *observation,
447+
const lis_intra_layer_fp32_view *view,
448+
lis_checkpoint_digest *out)
449+
{
450+
const lis_intra_layer_stage_info *stage_info;
451+
lis_sha256_context context;
452+
lis_status status;
453+
size_t precision_length = 0U;
454+
size_t logical_indices[LIS_INTRA_LAYER_MAX_RANK] = {0U};
455+
size_t logical_index;
456+
unsigned char zero = 0U;
457+
458+
if (out == NULL) {
459+
return LIS_STATUS_INVALID_ARGUMENT;
460+
}
461+
memset(out, 0, sizeof(*out));
462+
status = lis_intra_layer_digest_validate_inputs(
463+
record, observation, view, &stage_info, &precision_length);
464+
if (status != LIS_STATUS_OK) {
465+
return status;
466+
}
467+
status = lis_intra_layer_digest_preflight_stream_size(
468+
observation, stage_info, precision_length);
469+
if (status != LIS_STATUS_OK) {
470+
return status;
471+
}
472+
473+
lis_sha256_init(&context);
474+
lis_sha256_update(
475+
&context, (const unsigned char *)LIS_INTRA_LAYER_DIGEST_DOMAIN_TAG,
476+
strlen(LIS_INTRA_LAYER_DIGEST_DOMAIN_TAG));
477+
lis_sha256_update(&context, &zero, 1U);
478+
lis_checkpoint_digest_update_tag(&context,
479+
LIS_INTRA_LAYER_DIGEST_VERSION);
480+
lis_checkpoint_digest_update_tag(&context, LIS_INTRA_LAYER_LAYOUT_NAME);
481+
lis_checkpoint_digest_update_u64_le(&context,
482+
LIS_INTRA_LAYER_LAYOUT_VERSION);
483+
lis_checkpoint_digest_update_tag(&context,
484+
LIS_INTRA_LAYER_STAGE_TAXONOMY);
485+
lis_checkpoint_digest_update_tag(&context, LIS_INTRA_LAYER_MODEL_FAMILY);
486+
lis_checkpoint_digest_update_tag(&context, record->precision_path);
487+
lis_checkpoint_digest_update_tag(&context,
488+
LIS_INTRA_LAYER_PHASE_DECODE_NAME);
489+
lis_checkpoint_digest_update_u64_le(
490+
&context, (uint64_t)observation->runtime_checkpoint_step);
491+
lis_checkpoint_digest_update_u64_le(
492+
&context, (uint64_t)observation->layer_index);
493+
lis_checkpoint_digest_update_tag(&context, stage_info->stage_id);
494+
lis_checkpoint_digest_update_tag(&context, stage_info->tensor_role);
495+
lis_checkpoint_digest_update_u64_le(
496+
&context, (uint64_t)observation->batch_index);
497+
lis_checkpoint_digest_update_u64_le(
498+
&context, (uint64_t)observation->sequence_index);
499+
lis_checkpoint_digest_update_u64_le(
500+
&context, (uint64_t)observation->token_position);
501+
lis_checkpoint_digest_update_u64_le(
502+
&context, (uint64_t)observation->stage_order);
503+
lis_checkpoint_digest_update_u64_le(
504+
&context, (uint64_t)observation->execution_ordinal);
505+
lis_checkpoint_digest_update_u64_le(&context,
506+
(uint64_t)observation->rank);
507+
for (logical_index = 0U; logical_index < observation->rank;
508+
++logical_index) {
509+
lis_checkpoint_digest_update_u64_le(
510+
&context, (uint64_t)observation->shape[logical_index]);
511+
}
512+
lis_checkpoint_digest_update_tag(
513+
&context, LIS_CHECKPOINT_DIGEST_OBSERVED_DTYPE);
514+
lis_checkpoint_digest_update_tag(&context,
515+
LIS_CHECKPOINT_DIGEST_BYTE_ORDER);
516+
lis_checkpoint_digest_update_u64_le(
517+
&context, (uint64_t)observation->element_count);
518+
519+
for (logical_index = 0U;
520+
logical_index < observation->element_count;
521+
++logical_index) {
522+
size_t physical_offset = 0U;
523+
size_t dimension;
524+
uint32_t bits = 0U;
525+
unsigned char bytes[4];
526+
527+
for (dimension = 0U; dimension < observation->rank; ++dimension) {
528+
physical_offset += logical_indices[dimension] *
529+
view->element_strides[dimension];
530+
}
531+
memcpy(&bits, view->data + physical_offset, sizeof(bits));
532+
if ((bits & UINT32_C(0x7f800000)) == UINT32_C(0x7f800000) &&
533+
(bits & UINT32_C(0x007fffff)) != 0U) {
534+
bits = UINT32_C(0x7fc00000);
535+
}
536+
bytes[0] = (unsigned char)(bits & UINT32_C(0xff));
537+
bytes[1] = (unsigned char)((bits >> 8U) & UINT32_C(0xff));
538+
bytes[2] = (unsigned char)((bits >> 16U) & UINT32_C(0xff));
539+
bytes[3] = (unsigned char)((bits >> 24U) & UINT32_C(0xff));
540+
lis_sha256_update(&context, bytes, sizeof(bytes));
541+
542+
for (dimension = observation->rank; dimension > 0U; --dimension) {
543+
const size_t current = dimension - 1U;
544+
545+
++logical_indices[current];
546+
if (logical_indices[current] < observation->shape[current]) {
547+
break;
548+
}
549+
logical_indices[current] = 0U;
550+
}
551+
}
552+
lis_sha256_final(&context, out->bytes);
553+
out->valid = 1;
554+
return LIS_STATUS_OK;
555+
}
556+
207557
lis_status lis_checkpoint_digest_fp32(
208558
const char *tensor_role,
209559
const size_t *shape,

srcs/includes/lis/intra_layer_trace.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
* emission of the two additive blocks "intra_layer_checkpoint_layout" and
1212
* "intra_layer_trace".
1313
*
14-
* It carries a caller-supplied checkpoint digest and never computes one: no
15-
* entry point reads tensor elements, and lis_intra_layer_fp32_view is validated
16-
* for declared span coherence without ever dereferencing its data pointer. The
17-
* intra-layer digest stream and its computation are a separate work package.
14+
* The record module carries a caller-supplied checkpoint digest and never
15+
* computes one while appending or emitting a record. The additive digest entry
16+
* point declared below is the sole operation that traverses an FP32 view; the
17+
* structural view validator itself never dereferences the data pointer.
1818
*
1919
* The include list below is deliberately minimal: it is the mechanism that
2020
* keeps runtime, loader, and CLI state out of this module.
@@ -198,6 +198,17 @@ typedef struct lis_intra_layer_trace_record {
198198
_Static_assert(sizeof(lis_intra_layer_trace_record) <= 16384U,
199199
"intra-layer record must stay a small fixed-size object");
200200

201+
/*
202+
* Computes the frozen contextual/strided P4 digest for one observation. The
203+
* record must be ACTIVE and the observation coordinate must match its target.
204+
* On every failure, a non-NULL output is left invalid and zero-filled.
205+
*/
206+
lis_status lis_intra_layer_checkpoint_digest_fp32(
207+
const lis_intra_layer_trace_record *record,
208+
const lis_intra_layer_observation *observation,
209+
const lis_intra_layer_fp32_view *view,
210+
lis_checkpoint_digest *out);
211+
201212
/*
202213
* JSON primitives injected by the caller. The layer-trace writer passes its own
203214
* existing escaper and %.6g-or-null float writer, so the intra blocks are

0 commit comments

Comments
 (0)