aboutsummaryrefslogtreecommitdiffstats
path: root/docs/design/003-module-contract.md
blob: 1fcdb76bde7389ac277c1f586e4bf45fb679ee2f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# 모듈 계약 초안 0.1

상태: **제안**. 이 문서는 논리적 호출·수명·호환 조건과 데이터 레코드를 정한다.
동적 로더·네이티브 함수 테이블·Wasm 모듈 연결은 아직 구현하지 않았다.
[C17 레코드 헤더](abi-v0.h)는 데이터 표현을 검토·컴파일하기 위한 초안이며 실행 가능한 모듈 SDK가 아니다.

## 1. 세 가지 경계

| 경계 | 첫 방침 |
|---|---|
| Pure 내부 연산·모듈 | 값 종류, 버퍼 접근, 길이, 예산, 진행·오류·완료 의미를 명시 |
| 플랫폼의 네이티브 함수 호출 | 해당 타깃의 기존 C 호출 규약을 사용. 레지스터·스택 규약을 새로 발명하지 않음 |
| 저장·전송 형식 | 별도 버전·필드 폭·바이트 순서·검증·호환 규칙을 설계한 뒤 도입 |

구조체·포인터를 파일로 덤프하는 것을 영속 형식으로 삼지 않는다. 반대로 같은 프로세스에서
모듈을 호출할 때마다 데이터 전체를 직렬화할 필요도 없다.
Arm의 호출 규약도 데이터 모델·포인터·정렬·패딩을 다룬다. 이것이 Apple을 포함한 모든
플랫폼의 호출 차이를 없애주는 것은 아니므로 타깃별 경계를 검증해야 한다.
[AAPCS64 고정 원문](https://github.com/ARM-software/abi-aa/blob/0948ccc42ed2ea6f1ae88a3cbd5438d904709112/aapcs64/aapcs64.rst#L635-L695)

Wasm 선형 메모리의 숫자 저장은 little-endian이다. Wasm 어댑터는 그 규칙을 따르며,
네이티브 레코드를 포인터 그대로 다른 인스턴스나 프로세스에 넘기지 않는다.
[WebAssembly 숫자 저장](https://webassembly.github.io/spec/core/exec/numerics.html#storage)

## 2. 프로파일과 버전

모듈마다 다음 정보를 로드 전에 확인한다.

- 연산 계약 버전과 프로파일 ID. 첫 제안은 `0.1 / byte_stream=1`이다.
- 모듈 구현 식별자·빌드 해시·설정, 상태 스키마 식별자. 실행·재생 기록에 보관한다.
- 입력·출력 종류와 개수, 구간 최대 크기, 정렬, 문맥·임시 공간 예산.
- 동기/비동기 지원, 종료·취소 동작, 모듈 내부 외부 할당의 계측 가능 여부.

`0.x` 동안 호환성은 정확한 버전 일치로만 허용한다. 임의로 더 높은 minor를 받아들이지 않는다.
기능 필드·상태·플래그를 알 수 없으면 작업 시작 전에 `UNSUPPORTED`로 거절한다.
이후 호환 확장을 도입하려면 필수·선택 기능의 구분과 구버전 검사를 별도로 정한다.

현재 Workbench의 `revision`은 순환하는 적용 횟수다. ABI 버전·상태 스키마·비동기 티켓의
대체물로 사용하지 않는다. 현재 중간 명령 번호나 Zig `Program` 메모리 배치를 모듈 파일 형식으로 고정하지 않는다.

## 3. 첫 바이트 프로파일의 데이터 레코드

모든 필드는 고정 폭 정수다. 네이티브에서는 native-endian이며 C의 기본 정렬을 사용한다.
패킹 속성·C enum·bool·플랫폼별 `long`·포인터를 레코드에 넣지 않는다.
동일한 숫자 폭만으로 호출 ABI가 같아지는 것은 아니며, 함수 경계 검증은 별도다.

| 레코드 | 크기 | 뜻 |
|---|---:|---|
| `pure_handle_v0` | 8 B | 슬롯·세대. 실행 영역 안에서만 유효 |
| `pure_span_v0` | 16 B | 버퍼 핸들·오프셋·길이 |
| `pure_step_result_v0` | 24 B | 레코드 크기·스트림 상태·오류·이번 소비량·유효 생성량·예약 필드 |
| `pure_stream_limits_v0` | 64 B | 레코드 크기·버전·프로파일·개수·예산·구간 상한·정렬 |

이는 헤더의 크기 요구조건이며 메모리 사용량·전송량의 측정값이 아니다.
입출력 수와 `max_pending_per_context`는 첫 프로파일에서 각각 1이어야 한다.
`flags`와 `reserved`는 0이어야 한다. `struct_bytes`는 해당 크기와 정확히 일치해야 한다.
`max_input_bytes/max_output_bytes`는 1 이상이고 협상한 예산 이하, `buffer_alignment`는
1 이상의 2의 거듭제곱이어야 한다. 호스트가 보장할 수 없는 정렬·예산은 로드 또는 open에서 거절한다.
0인 문맥·임시 예산은 해당 종류의 할당을 허용하지 않는다는 뜻이지 무제한이 아니다.

한 요청의 길이는 `u32`, 모듈 예산·스트림의 누적 길이는 검사된 `u64`다.
JavaScript 어댑터는 `u64`를 BigInt 또는 명시적 두 워드로 전달하고 Number로 조용히 축약하지 않는다.
크기·개수·누적 산술은 실제 접근·할당 전에 검사한다.

결과 검증 규칙:

- `status`는 헤더의 여섯 값 중 하나다. `FAILED`에만 0이 아닌 `error`가 필요하고 다른 상태는 0이다.
- `consumed/produced`는 제공한 길이 이하이고 `MORE`는 둘 중 하나가 0보다 커야 한다.
- `reserved=0`, 레코드 크기가 일치해야 한다. 실패해도 검증된 부분 진행을 정확히 보존한다.
- 잘못된 완료는 모듈 프로토콜 오류다. 데이터는 공개하지 않고 문맥을 실패 상태로 만든다.
  접근 종료를 보장하는 신뢰 가능한 완료까지 대여를 보유한다. 길이 오류를 핑계로 진행 중 버퍼를 재사용하지 않는다.
- 헤더의 오류 번호는 이 초안의 별도 네임스페이스다. 현재 scalar VM의 오류 번호와 혼용하지 않는다.

## 4. 논리적 제어 API

아래는 다음 구현의 호출 의미다. 네이티브 C 함수 시그니처나 동적 로딩 심볼을 확정한 선언은 아니다.
첫 구현은 정적으로 등록한 내부 어댑터로 이 의미부터 검증한다.

| 호출 | 결과와 수명 |
|---|---|
| `describe(module)` | 계약·기능·한도·구현 식별. 작업·대여 생성 없음 |
| `open(module, config, budget)` | 문맥 생성 또는 거절. 성공 시 문맥·숨은 내부 메모리의 예산 책임 시작 |
| `submit(context, input, output, END_INPUT)` | 런타임이 거절하거나 티켓을 접수. 준비/확정/백엔드 제출 구분은 연산 명세를 따름 |
| `cancel(ticket)` | 취소 요청을 전달한 결과. 스트림 결과나 버퍼 회수가 아님 |
| `reap(ticket)` | 아직 완료가 없으면 `WOULD_BLOCK`; 있으면 결과 회수·유효 길이 반영·대여 해제 |
| `close(context)` | 진행 작업이 있으면 `BUSY`; 없으면 내부 문맥·장기 자원 반환. 이후 핸들 무효 |

사용하지 않는 입력이나 출력도 유효한 버퍼 핸들과 길이 0 구간으로 표시한다.
`open/submit`의 일시적 풀 포화는 `WOULD_BLOCK`, 고정 한도를 넘는 요구는 `LIMIT`,
이미 진행 요청이 있는 문맥은 `BUSY`, 잘못된 핸들·범위는 `INVALID`다.
이 접수 응답들은 `pure_step_result_v0`의 스트림 상태와 별개이며 해당 C 응답 레코드는 후속 구현에서 정한다.

동기 어댑터도 `submit` 시 즉시 결과를 완료 슬롯에 보관하고 `reap`에서 전달할 수 있다.
호스트가 최적화로 내부 호출을 합치더라도 수명·오류·관찰 가능한 순서는 유지해야 한다.
개별 문맥은 순차 처리하며 다른 문맥 사이의 완료 순서는 제출 순서와 같다고 가정하지 않는다.

`reap`은 요청의 완료다. 결과가 `NEED_INPUT`이면 문맥은 살아 있으며 다음 입력을 받을 수 있다.
문맥은 `DONE/FAILED/CANCELLED` 이후에도 `close`까지 예산을 차지한다.
프로그램·모듈 교체는 관련 요청·대여·열린 문맥이 모두 해소된 뒤에만 허용한다.

## 5. 확장 범위

압축·인증 복호화·문서·미디어는 포맷 처리 모듈의 후보이며 모두 같은 복잡도를 갖지는 않는다.
여러 평면의 영상, 탐색 가능한 문서, 파일·네트워크의 부분 쓰기는 추가 프로파일이 필요하다.
현재 바이트 프로파일에 의미가 다른 모든 출력을 억지로 넣지 않는다.

Pure 자체를 작은 OS로 설계한다는 [현재 방향](007-execution-domains.md)에 따라 실행·자원·
통신의 운영 모델은 Pure가 소유한다. 현재 호스트 OS는 구동 수단과 제약이며, 모듈의 의미를
호스트의 프로세스·파일 디스크립터에 고정하지 않는다. 호스트 구동, kernel-bypass, 직접 하드웨어
구동은 이를 실현하는 경로로 구분한다. 부팅·드라이버의 범위나 일정은 이 모듈 계약으로 확정하지 않는다.

## 6. ABI를 구현했다고 말할 수 있는 조건

레코드의 `_Static_assert`가 타깃별로 컴파일되는 것은 데이터 레이아웃의 제한된 검사다.
실제 함수 호출·언어 간 호환·버전 협상·대여·완료의 검증을 대신하지 않는다.
후속 native adapter에서 함수 테이블 크기·호출 규약·오류 경계·호스트 할당 콜백·리소스 해제 책임을
구체적인 선언으로 작성하고, 독립 C 생산자와 Zig 소비자의 실제 왕복 호출을 검사해야 한다.
Wasm 어댑터는 범위·리프트/로워·수명 검사와 복사량을 별도로 측정해야 한다.
이 단계 전에는 안정된 ABI·플러그인 호환·새 포맷 지원을 구현 완료로 보고하지 않는다.