렉서 다음은 파서다. 노드는 한 배열에 살고 자식은 그 안의 인덱스다 -- 노드는 ^Node 를 들 수 없고(여럿이며 한 번씩 소유하지 않는다) &Node 도 들 수 없다(R4). 인덱스는 둘 다 아니다. 소스도 필드가 아니라 매 단계에 같이 다닌다. unit demo / fn answer @2 / let n = (+ 1 (* 2 3)) / return n / balanced 전위 표기로 다시 찍는 것이 시험의 요점이다. 1 + 2 * 3 이 어떻게 묶였는지는 그렇게만 보인다. 쓰면서 나온 컴파일러 버그 넷: 1. 다른 유닛의 타입을 필드로 쓰면 그 필드 타입이 영영 UNKNOWN 이었다. 필드 해석이 유닛마다 선언 직후에 돌아서, 아직 선언되지 않은 유닛의 타입을 찾다 실패하고 그 답을 굳혔다. 이제 모든 유닛이 선언을 마친 뒤에 한 번 푼다. 2. 그리고 그 해석은 타입을 선언한 유닛에서 해야 한다. 필드 타입은 그 유닛의 import 로 쓰였는데 아무 유닛에서나 풀고 있었다. 타입 계층에 enter/leave 콜백을 두고 체커가 그 자리로 데려간다. 3. cycle_state 를 재귀 검사와 크기 계산이 같이 썼다. 첫 번째가 보는 중인 구조체 가 두 번째에게는 다 끝난 것으로 보여서, 필드가 하나뿐인 것처럼 1 바이트로 자리를 잡았다 -- Parser 가 그래서 자기 토큰을 밟았다. layout_state 로 나눴다. 4. 다른 유닛의 상수(ast.NONE)를 lowering 이 필드 접근으로 봤다. 체커가 이미 링크 이름을 붙여두었으니 그것이 있으면 전역이다. 그리고 R1 을 실제로 지키게 했다: 소유자를 놓으면 그것이 가진 것도 놓는다. 전에는 자기 drop 이 있거나 자기가 owned 일 때만이어서, drop 을 가진 타입을 필드로 담은 구조체는 그것을 놓을 방법이 없었다(drop 은 손으로 못 부른다). 이제 release_at 이 drop 을 부르고 필드로 내려간다. 그 덕에 List/Arena/Map 의 drop 이 전부 필요 없어져서 지웠다 -- 버퍼가 owned 이니 R1 이 알아서 한다. 221/221, 29/29.
171 lines
6.8 KiB
C
171 lines
6.8 KiB
C
#ifndef FE_LOWERPRI_H
|
|
#define FE_LOWERPRI_H
|
|
|
|
/* Lowering's own vocabulary, shared by the files it is split across. */
|
|
|
|
#include "lower.h"
|
|
#include "m7.h"
|
|
#include "own.h"
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
#include "m7.h"
|
|
#include "own.h"
|
|
#include <stdio.h>
|
|
|
|
/* ------------------------------------------------------------------------- *
|
|
* Lowering
|
|
*
|
|
* One function at a time, one statement at a time. A `Slot` is what an
|
|
* expression produced: either a value already in a temporary, or a place in
|
|
* memory that a value can be read from or written to. Aggregates are always
|
|
* places -- they are never carried in a temporary, because a temporary is a
|
|
* register and an aggregate does not fit in one.
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
|
|
typedef struct LowerVar {
|
|
const char *cname;
|
|
unsigned local;
|
|
/* An aggregate parameter arrives as an address, so the slot holds a
|
|
pointer and the value is one dereference away. */
|
|
int by_address;
|
|
} LowerVar;
|
|
|
|
typedef struct Lower {
|
|
FeCheck *c;
|
|
FeIrModule *m;
|
|
FeIrFunc *fn;
|
|
FeIrBlock *b; /* the block being appended to */
|
|
FeType *ret_type;
|
|
unsigned ret_local; /* hidden result address, when returning mem */
|
|
/* These three grow. A fixed size here does not report a program that is
|
|
too big -- it quietly drops what does not fit and generates wrong code,
|
|
which is the worst way for a limit to be reached. */
|
|
LowerVar *vars;
|
|
unsigned var_count;
|
|
unsigned var_capacity;
|
|
/* Loop targets, for break and continue. */
|
|
unsigned break_target[32];
|
|
unsigned continue_target[32];
|
|
unsigned loop_depth;
|
|
/* What a scope still owes when it ends: `defer` blocks to run and owned
|
|
values to release, in the order they were written. Every exit path runs
|
|
what is live, last first.
|
|
|
|
A drop carries a flag beside the value. The flag is set when the value
|
|
is stored and cleared wherever it is moved away, so the release happens
|
|
exactly on the paths where the value is still there -- which is not
|
|
something the shape of the code can tell you on its own. */
|
|
struct {
|
|
FeNode *block; /* a `defer`, when set */
|
|
unsigned local; /* the owned value, otherwise */
|
|
unsigned flag;
|
|
FeType *type;
|
|
} *owed;
|
|
unsigned owed_count;
|
|
unsigned owed_capacity;
|
|
/* Every `error.Name` used anywhere in the build, sorted, numbered from one.
|
|
SPEC 4.6: the names are collected rather than declared, and the order is
|
|
fixed by the spelling so that the same program always gets the same
|
|
codes however the build was ordered. */
|
|
const char **error_names;
|
|
unsigned error_count;
|
|
unsigned error_capacity;
|
|
int failed;
|
|
} Lower;
|
|
|
|
typedef struct Slot {
|
|
int is_place;
|
|
unsigned temp; /* the value, when is_place is 0 */
|
|
FeIrPlace place; /* where it lives, when is_place is 1 */
|
|
FeIrType type;
|
|
unsigned long size; /* for FE_IR_MEM */
|
|
} Slot;
|
|
|
|
|
|
|
|
/* A slice is a pointer and a length, in that order. Both the compiler and the
|
|
runtime read it this way, so the offsets live here and nowhere else. */
|
|
#define SLICE_PTR_OFFSET 0L
|
|
#define SLICE_LEN_OFFSET 4L
|
|
|
|
/* Grow one of the checker's own arrays. Returns zero when there is no more
|
|
memory, which the caller reports rather than ignores. */
|
|
int lower_reserve(Lower *L, void **items, unsigned *capacity, unsigned needed,
|
|
unsigned long item_size);
|
|
|
|
/* Every definition in lowering, so the split files can see each other. */
|
|
FeIrType tag_type_of(const FeType *t);
|
|
int struct_is_generic(const FeNode *decl);
|
|
void lower_if_let(Lower *L, FeNode *n);
|
|
unsigned wrapper_tag(Lower *L, Slot w, const FeType *t, FeNode *n);
|
|
void bind_payload(Lower *L, Slot subject, const FeType *t,
|
|
const FeVariantType *v, FeNode *arm);
|
|
void fail(Lower *L, const char *why, FeNode *n);
|
|
FeIrType ir_type_of(const FeType *t);
|
|
int enum_has_payload(const FeType *t);
|
|
FeIrType ir_type(const FeType *t);
|
|
unsigned long ir_size(const FeType *t);
|
|
unsigned ir_align(const FeType *t);
|
|
int type_is_unsigned(const FeType *t);
|
|
Slot slot_value(unsigned temp, FeIrType t);
|
|
Slot slot_place(FeIrPlace p, FeIrType t, unsigned long size);
|
|
Slot slot_void(void);
|
|
unsigned as_value(Lower *L, Slot s, FeNode *n);
|
|
unsigned as_address(Lower *L, Slot s, FeNode *n);
|
|
int needs_release(const FeType *t);
|
|
unsigned declare_var(Lower *L, const char *cname, const FeType *t,
|
|
const char *name);
|
|
int release_flag(Lower *L, unsigned local, unsigned *flag);
|
|
LowerVar *find_var(Lower *L, const char *cname);
|
|
FeIrBlock *new_block(Lower *L);
|
|
void guard(Lower *L, unsigned ok, FeIrTrap reason, unsigned long line);
|
|
FeIrType tag_type(const FeType *t);
|
|
int uses_niche(const FeType *t);
|
|
unsigned scratch(Lower *L, const FeType *t, const char *why);
|
|
void indexable_parts(Lower *L, Slot base, const FeType *t,
|
|
unsigned *data, unsigned *length, FeNode *n);
|
|
void note_error_name(Lower *L, const char *name);
|
|
void collect_error_names(Lower *L, FeNode *n);
|
|
long error_code(Lower *L, const char *name);
|
|
FeIrOp binary_op(const char *op, int *is_cmp);
|
|
long literal_value(FeNode *n);
|
|
Slot lower_logical(Lower *L, FeNode *n, int is_and);
|
|
int lower_builtin(Lower *L, FeNode *n, Slot *out);
|
|
int is_mem_call(const FeNode *n, const char *what);
|
|
Slot allocation_result(Lower *L, FeNode *n, unsigned pointer);
|
|
int lower_mem(Lower *L, FeNode *n, Slot *out);
|
|
void emit_text(Lower *L, unsigned handle, const char *text,
|
|
unsigned long len);
|
|
void emit_value_text(Lower *L, unsigned handle, FeNode *arg, int verb);
|
|
int lower_print(Lower *L, FeNode *n, Slot *out);
|
|
Slot lower_call(Lower *L, FeNode *n);
|
|
Slot lower_expr(Lower *L, FeNode *n);
|
|
Slot lower_expr_core(Lower *L, FeNode *n);
|
|
const char *drop_name(Lower *L, const FeType *t);
|
|
void release_at(Lower *L, const FeType *t, FeIrPlace at);
|
|
void run_deferred(Lower *L, unsigned from);
|
|
Slot wrap_context(Lower *L, Slot v, FeNode *n);
|
|
unsigned wrapper_tag(Lower *L, Slot w, const FeType *t, FeNode *n);
|
|
Slot wrapper_payload(Lower *L, Slot w, const FeType *t);
|
|
void return_error(Lower *L, unsigned err, FeNode *n);
|
|
Slot lower_try(Lower *L, FeNode *n);
|
|
Slot lower_lazy(Lower *L, FeNode *n, int is_catch);
|
|
void store_into(Lower *L, FeIrPlace dst, Slot value, FeNode *n,
|
|
unsigned long size);
|
|
void lower_return(Lower *L, FeNode *n);
|
|
void lower_if(Lower *L, FeNode *n);
|
|
void lower_while(Lower *L, FeNode *n);
|
|
Slot lower_slice(Lower *L, FeNode *n);
|
|
void lower_for(Lower *L, FeNode *n);
|
|
void lower_match(Lower *L, FeNode *n);
|
|
void lower_stmt(Lower *L, FeNode *n);
|
|
void lower_global(Lower *L, FeNode *n);
|
|
int fn_is_generic(const FeNode *fn);
|
|
void lower_fn_as(Lower *L, FeNode *fn, const char *name);
|
|
void lower_fn(Lower *L, FeNode *fn);
|
|
|
|
#endif
|