• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mattwparas / steel / 11947392359

21 Nov 2024 05:44AM UTC coverage: 46.072% (-0.2%) from 46.234%
11947392359

Pull #291

github

web-flow
Merge 8d943de47 into 9a2bb2ef5
Pull Request #291: Add rustls library and fix free identifier error message

16 of 86 new or added lines in 6 files covered. (18.6%)

37 existing lines in 10 files now uncovered.

12334 of 26771 relevant lines covered (46.07%)

415077.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.17
/crates/steel-core/src/compiler/program.rs
1
use crate::core::instructions::u24;
2
use crate::core::labels::Expr;
3
use crate::gc::Shared;
4
use crate::parser::span_visitor::get_span;
5
use crate::rvals::{Result, SteelComplex};
6
use crate::{
7
    compiler::constants::ConstantMap,
8
    core::{instructions::Instruction, opcode::OpCode},
9
    stop, SteelVal,
10
};
11
use crate::{core::instructions::DenseInstruction, parser::span::Span};
12
use crate::{
13
    parser::{
14
        ast::ExprKind,
15
        interner::InternedString,
16
        parser::{RawSyntaxObject, SyntaxObject},
17
        tokens::TokenType,
18
    },
19
    rvals::IntoSteelVal,
20
};
21

22
use num::{BigInt, BigRational, Rational32};
23
use serde::{Deserialize, Serialize};
24
use std::{collections::HashMap, convert::TryInto, time::SystemTime};
25
use steel_parser::tokens::{IntLiteral, NumberLiteral, RealLiteral};
26

27
#[cfg(feature = "profiling")]
28
use std::time::Instant;
29

30
#[cfg(feature = "profiling")]
31
use log::{debug, log_enabled};
32

33
use super::{compiler::DebruijnIndicesInterner, map::SymbolMap};
34

35
const _TILE_SUPER_INSTRUCTIONS: bool = true;
36

37
pub fn number_literal_to_steel(n: &NumberLiteral) -> Result<SteelVal> {
114,831✔
38
    // real_to_steel does some cloning of bignums. It may be possible to optimize this away.
39
    let real_to_steel = |re: &RealLiteral| match re {
343,323✔
40
        RealLiteral::Int(IntLiteral::Small(i)) => i.into_steelval(),
113,062✔
41
        RealLiteral::Int(IntLiteral::Big(i)) => i.clone().into_steelval(),
280✔
42
        RealLiteral::Rational(n, d) => match (n, d) {
470✔
43
            (IntLiteral::Small(n), IntLiteral::Small(d)) => {
450✔
44
                match (i32::try_from(*n), i32::try_from(*d)) {
450✔
45
                    (Ok(n), Ok(0)) => {
×
46
                        stop!(BadSyntax => format!("division by zero in {:?}/0", n))
×
47
                    }
48
                    (Ok(n), Ok(d)) => Rational32::new(n, d).into_steelval(),
430✔
49
                    _ => BigRational::new(BigInt::from(*n), BigInt::from(*d)).into_steelval(),
20✔
50
                }
51
            }
52
            (IntLiteral::Small(n), IntLiteral::Big(d)) => {
×
53
                BigRational::new(BigInt::from(*n), *d.clone()).into_steelval()
×
54
            }
55
            (IntLiteral::Big(n), IntLiteral::Small(d)) => {
20✔
56
                BigRational::new(*n.clone(), BigInt::from(*d)).into_steelval()
20✔
57
            }
58
            (IntLiteral::Big(n), IntLiteral::Big(d)) => {
×
59
                BigRational::new(*n.clone(), *d.clone()).into_steelval()
×
60
            }
61
        },
62
        RealLiteral::Float(f) => f.into_steelval(),
1,338✔
63
    };
64
    match n {
114,831✔
65
        NumberLiteral::Real(re) => real_to_steel(re),
114,512✔
66
        NumberLiteral::Complex(re, im) => SteelComplex {
67
            re: real_to_steel(re)?,
319✔
68
            im: real_to_steel(im)?,
319✔
69
        }
70
        .into_steelval(),
71
    }
72
}
73

74
/// Evaluates an atom expression in given environment.
75
fn eval_atom(t: &SyntaxObject) -> Result<SteelVal> {
112,002✔
76
    match &t.ty {
112,002✔
77
        TokenType::BooleanLiteral(b) => Ok((*b).into()),
30,507✔
78
        TokenType::Number(n) => number_literal_to_steel(n),
40,647✔
79
        TokenType::StringLiteral(s) => Ok(SteelVal::StringV(s.to_string().into())),
39,930✔
80
        TokenType::CharacterLiteral(c) => Ok(SteelVal::CharV(*c)),
854✔
81
        // TODO: Keywords shouldn't be misused as an expression - only in function calls are keywords allowed
82
        TokenType::Keyword(k) => Ok(SteelVal::SymbolV(k.clone().into())),
64✔
83
        what => {
×
84
            println!("getting here in the eval_atom - code_generator");
×
85
            stop!(UnexpectedToken => what; t.span)
×
86
        }
87
    }
88
}
89

90
pub fn specialize_read_local(instructions: &mut [Instruction]) {
48,440✔
91
    for i in 0..instructions.len() {
3,579,789✔
92
        let read_local = instructions.get(i);
3,531,349✔
93

94
        match read_local {
3,531,349✔
95
            Some(Instruction {
96
                op_code: OpCode::MOVEREADLOCAL,
97
                payload_size,
109,766✔
98
                ..
99
            }) => {
100
                let op_code = match payload_size.to_u32() {
172,475✔
101
                    0 => OpCode::MOVEREADLOCAL0,
31,375✔
102
                    1 => OpCode::MOVEREADLOCAL1,
15,216✔
103
                    2 => OpCode::MOVEREADLOCAL2,
9,345✔
104
                    3 => OpCode::MOVEREADLOCAL3,
6,773✔
105
                    _ => continue,
47,057✔
106
                };
107

108
                if let Some(x) = instructions.get_mut(i) {
62,709✔
109
                    x.op_code = op_code;
110
                }
111
            }
112

113
            Some(Instruction {
114
                op_code: OpCode::READLOCAL,
115
                payload_size,
128,562✔
116
                ..
117
            }) => {
118
                let op_code = match payload_size.to_u32() {
92,314✔
119
                    0 => OpCode::READLOCAL0,
54,223✔
120
                    1 => OpCode::READLOCAL1,
23,610✔
121
                    2 => OpCode::READLOCAL2,
10,220✔
122
                    3 => OpCode::READLOCAL3,
4,261✔
123
                    _ => continue,
36,248✔
124
                };
125

126
                if let Some(x) = instructions.get_mut(i) {
92,314✔
127
                    x.op_code = op_code;
128
                }
129
            } // instructions[i + 1].op_code = OpCode::PASS;
130
            // instructions[i + 2].op_code = OpCode::PASS;
131
            // instructions[i + 4].op_code = OpCode::PASS;
132
            _ => continue,
3,293,021✔
133
        }
134
    }
135
}
136

137
pub fn specialize_constants(instructions: &mut [Instruction]) -> Result<()> {
48,440✔
138
    for instruction in instructions.iter_mut() {
3,579,789✔
139
        match instruction {
112,002✔
140
            Instruction {
141
                op_code: OpCode::PUSHCONST,
142
                contents:
143
                    Some(Expr::Atom(SyntaxObject {
144
                        ty: TokenType::Identifier(_),
145
                        ..
146
                    })),
147
                ..
148
            } => continue,
×
149
            Instruction {
150
                op_code: OpCode::PUSHCONST,
151
                contents: Some(Expr::Atom(syn)),
112,002✔
152
                ..
153
            } => {
154
                let value = eval_atom(syn)?;
112,002✔
155
                let opcode = match &value {
22,167✔
156
                    SteelVal::IntV(0) => OpCode::LOADINT0,
9,917✔
157
                    SteelVal::IntV(1) => OpCode::LOADINT1,
8,548✔
158
                    SteelVal::IntV(2) => OpCode::LOADINT2,
3,702✔
159
                    _ => continue,
89,835✔
160
                };
161
                instruction.op_code = opcode;
162
            }
163
            _ => continue,
3,419,347✔
164
        }
165
    }
166
    Ok(())
48,440✔
167
}
168

169
pub fn convert_call_globals(instructions: &mut [Instruction]) {
24,245✔
170
    if instructions.is_empty() {
24,245✔
171
        return;
×
172
    }
173

174
    for i in 0..instructions.len() - 1 {
1,744,045✔
175
        let push = instructions.get(i);
1,744,045✔
176
        let func = instructions.get(i + 1);
1,744,045✔
177

178
        match (push, func) {
1,744,045✔
179
            (
180
                Some(Instruction {
181
                    op_code: OpCode::PUSH,
182
                    payload_size: index,
261,180✔
183
                    contents: Some(Expr::Atom(ident)),
261,180✔
184
                    ..
261,180✔
185
                }),
186
                Some(Instruction {
261,180✔
187
                    op_code: OpCode::FUNC,
261,180✔
188
                    payload_size: arity,
261,180✔
189
                    ..
261,180✔
190
                }),
191
            ) => {
261,180✔
192
                let arity = arity.to_usize();
261,180✔
193
                let index = *index;
261,180✔
194

195
                if let TokenType::Identifier(ident) = ident.ty {
522,360✔
196
                    match ident {
197
                        _ if ident == *PRIM_CONS_SYMBOL && arity == 2 => {
541✔
198
                            if let Some(x) = instructions.get_mut(i) {
1,082✔
199
                                x.op_code = OpCode::CONS;
200
                                x.payload_size = u24::from_u32(2);
201
                                continue;
202
                            }
203
                        }
204

205
                        // Specialize lists, cons, hashmap, etc. - anything that we expect to be used often in
206
                        // real code.
207
                        // _ if ident == *PRIM_LIST_SYMBOL => {
208
                        //     if let Some(x) = instructions.get_mut(i) {
209
                        //         x.op_code = OpCode::LIST;
210
                        //         x.payload_size = arity;
211
                        //         continue;
212
                        //     }
213
                        // }
214
                        _ if ident == *BOX || ident == *PRIM_BOX && arity == 1 => {
517,211✔
215
                            if let Some(x) = instructions.get_mut(i) {
8,134✔
216
                                x.op_code = OpCode::NEWBOX;
217
                                continue;
218
                            }
219
                        }
220

221
                        _ if ident == *UNBOX || ident == *PRIM_UNBOX && arity == 1 => {
503,187✔
222
                            if let Some(x) = instructions.get_mut(i) {
19,914✔
223
                                x.op_code = OpCode::UNBOX;
224
                                continue;
225
                            }
226
                        }
227

228
                        _ if ident == *SETBOX || ident == *PRIM_SETBOX && arity == 1 => {
489,491✔
229
                            if let Some(x) = instructions.get_mut(i) {
7,478✔
230
                                x.op_code = OpCode::SETBOX;
231
                                continue;
232
                            }
233
                        }
234

235
                        _ if ident == *PRIM_CAR && arity == 1 => {
244,495✔
236
                            if let Some(x) = instructions.get_mut(i) {
3,238✔
237
                                x.op_code = OpCode::CAR;
238
                                continue;
239
                            }
240
                        }
241

242
                        _ if ident == *PRIM_CDR && arity == 1 => {
242,700✔
243
                            if let Some(x) = instructions.get_mut(i) {
2,886✔
244
                                x.op_code = OpCode::CDR;
245
                                continue;
246
                            }
247
                        }
248

249
                        _ if ident == *PRIM_NOT && arity == 1 => {
240,427✔
250
                            if let Some(x) = instructions.get_mut(i) {
1,226✔
251
                                x.op_code = OpCode::NOT;
252
                                continue;
253
                            }
254
                        }
255

256
                        _ if ident == *PRIM_NULL && arity == 1 => {
239,685✔
257
                            if let Some(x) = instructions.get_mut(i) {
968✔
258
                                x.op_code = OpCode::NULL;
259
                                continue;
260
                            }
261
                        }
262

263
                        // _ if ident == *CDR_SYMBOL || ident == *PRIM_CAR => {
264
                        //     if let Some(x) = instructions.get_mut(i) {
265
                        //         x.op_code = OpCode::CAR;
266
                        //         continue;
267
                        //     }
268
                        // }
269
                        _ => {
238,717✔
270
                            // println!("Converting call global: {}", ident);
271
                        }
272
                    }
273
                }
274

275
                // TODO:
276
                if let Some(x) = instructions.get_mut(i) {
477,434✔
277
                    x.op_code = OpCode::CALLGLOBAL;
278
                    x.payload_size = index;
279
                }
280

281
                if let Some(x) = instructions.get_mut(i + 1) {
238,717✔
282
                    // Leave this as the OpCode::FUNC;
283
                    // x.op_code = OpCode::Arity;
284
                    x.payload_size = u24::from_usize(arity);
285
                }
286
            }
287
            (
288
                Some(Instruction {
289
                    op_code: OpCode::PUSH,
290
                    payload_size: index,
38,837✔
291
                    contents: Some(Expr::Atom(ident)),
38,837✔
292
                    ..
38,837✔
293
                }),
294
                Some(Instruction {
38,837✔
295
                    op_code: OpCode::TAILCALL,
38,837✔
296
                    // payload_size: arity,
297
                    ..
38,837✔
298
                }),
299
            ) => {
38,837✔
300
                // let arity = *arity;
301
                let index = *index;
38,837✔
302

303
                if let TokenType::Identifier(ident) = ident.ty {
77,674✔
304
                    match ident {
305
                        _ if ident == *PRIM_CONS_SYMBOL => {
306
                            if let Some(x) = instructions.get_mut(i) {
52✔
307
                                x.op_code = OpCode::CONS;
308
                                x.payload_size = u24::from_u32(2);
309
                                continue;
310
                            }
311
                        }
312

313
                        _ if ident == *BOX || ident == *PRIM_BOX => {
77,622✔
314
                            if let Some(x) = instructions.get_mut(i) {
×
315
                                x.op_code = OpCode::NEWBOX;
316
                                continue;
317
                            }
318
                        }
319

320
                        _ if ident == *UNBOX || ident == *PRIM_UNBOX => {
77,284✔
321
                            if let Some(x) = instructions.get_mut(i) {
676✔
322
                                x.op_code = OpCode::UNBOX;
323
                                continue;
324
                            }
325
                        }
326

327
                        _ if ident == *SETBOX || ident == *PRIM_SETBOX => {
76,604✔
328
                            if let Some(x) = instructions.get_mut(i) {
684✔
329
                                x.op_code = OpCode::SETBOX;
330
                                continue;
331
                            }
332
                        }
333

334
                        _ if ident == *PRIM_CAR => {
38,131✔
335
                            if let Some(x) = instructions.get_mut(i) {
8✔
336
                                x.op_code = OpCode::CAR;
337
                                continue;
338
                            }
339
                        }
340

341
                        // Specialize lists, cons, hashmap, etc. - anything that we expect to be used often in
342
                        // real code.
343
                        _ if ident == *LIST_SYMBOL || ident == *PRIM_LIST_SYMBOL => {}
76,271✔
344

345
                        _ => {}
37,414✔
346
                    }
347
                }
348

349
                if let Some(x) = instructions.get_mut(i) {
76,254✔
350
                    x.op_code = OpCode::CALLGLOBALTAIL;
351
                    x.payload_size = index;
352
                }
353

354
                // if let Some(x) = instructions.get_mut(i + 1) {
355
                // x.op_code = OpCode::Arity;
356
                // x.payload_size = arity;
357
                // }
358
            }
359
            _ => {}
1,444,028✔
360
        }
361
    }
362
}
363

364
#[macro_export]
365
macro_rules! define_primitive_symbols {
366
    ($(($prim_name:tt, $name:tt) => $str:expr,) * ) => {
367
        $(
368
            pub static $name: once_cell::sync::Lazy<InternedString> = once_cell::sync::Lazy::new(|| InternedString::from_static($str));
×
369

370
            pub static $prim_name: once_cell::sync::Lazy<InternedString> = once_cell::sync::Lazy::new(|| InternedString::from_static(concat!("#%prim.", $str)));
44✔
371
        )*
372
    };
373
}
374

375
#[macro_export]
376
macro_rules! define_symbols {
377
    ($($name:tt => $str:expr,) * ) => {
378
        $(
379
            pub static $name: once_cell::sync::Lazy<InternedString> = once_cell::sync::Lazy::new(|| InternedString::from_static($str));
98✔
380
        )*
381
    };
382
}
383

384
define_primitive_symbols! {
385
    (PRIM_PLUS, PLUS) => "+",
386
    (PRIM_MINUS, MINUS) => "-",
387
    (PRIM_DIV, DIV) => "/",
388
    (PRIM_STAR, STAR) => "*",
389
    (PRIM_EQUAL, EQUAL) => "equal?",
390
    (PRIM_NUM_EQUAL, NUM_EQUAL) => "=",
391
    (PRIM_LTE, LTE) => "<=",
392
    (PRIM_CAR, CAR_SYMBOL) => "car",
393
    (PRIM_CDR, CDR_SYMBOL) => "cdr",
394
    (PRIM_NOT, NOT_SYMBOL) => "not",
395
    (PRIM_NULL, NULL_SYMBOL) => "null?",
396
}
397

398
define_symbols! {
399
    UNREADABLE_MODULE_GET => "##__module-get",
400
    STANDARD_MODULE_GET => "%module-get%",
401
    CONTRACT_OUT => "contract/out",
402
    REQUIRE_IDENT_SPEC => "%require-ident-spec",
403
    PROVIDE => "provide",
404
    FOR_SYNTAX => "for-syntax",
405
    PREFIX_IN => "prefix-in",
406
    ONLY_IN => "only-in",
407
    DATUM_SYNTAX => "datum->syntax",
408
    SYNTAX_SPAN => "#%syntax-span",
409
    IF => "if",
410
    DEFINE => "define",
411
    LET => "let",
412
    QUOTE =>"quote",
413
    RETURN => "return!",
414
    REQUIRE => "require",
415
    SET => "set!",
416
    PLAIN_LET => "%plain-let",
417
    LAMBDA => "lambda",
418
    LAMBDA_SYMBOL => "λ",
419
    LAMBDA_FN => "fn",
420
    BEGIN => "begin",
421
    DOC_MACRO => "@doc",
422
    REQUIRE_BUILTIN => "require-builtin",
423
    REQUIRE_DYLIB => "#%require-dylib",
424
    STRUCT_KEYWORD => "struct",
425
    BETTER_LAMBDA => "#%better-lambda",
426
    DEFINE_VALUES => "define-values",
427
    AS_KEYWORD => "as",
428
    SYNTAX_CONST_IF => "syntax-const-if",
429
    UNQUOTE => "unquote",
430
    UNQUOTE_COMMA => "#%unquote-comma",
431
    RAW_UNQUOTE => "#%unquote",
432
    UNQUOTE_SPLICING => "unquote-splicing",
433
    RAW_UNQUOTE_SPLICING => "#%unquote-splicing",
434
    QUASIQUOTE => "quasiquote",
435
    RAW_QUOTE => "#%quote",
436
    QUASISYNTAX => "quasisyntax",
437
    UNSYNTAX => "unsyntax",
438
    RAW_UNSYNTAX => "#%unsyntax",
439
    UNSYNTAX_SPLICING => "unsyntax-splicing",
440
    RAW_UNSYNTAX_SPLICING => "#%unsyntax-splicing",
441
    SYNTAX_QUOTE => "syntax",
442
    CONS_SYMBOL => "cons",
443
    PRIM_CONS_SYMBOL => "#%prim.cons",
444
    LIST_SYMBOL => "list",
445
    PRIM_LIST_SYMBOL => "#%prim.list",
446
    BOX => "#%box",
447
    PRIM_BOX => "#%prim.box",
448
    UNBOX => "#%unbox",
449
    PRIM_UNBOX => "#%prim.unbox",
450
    SETBOX => "#%set-box!",
451
    PRIM_SETBOX => "#%prim.set-box!",
452
    DEFMACRO => "defmacro",
453
    BEGIN_FOR_SYNTAX => "begin-for-syntax",
454
}
455

456
pub fn inline_num_operations(instructions: &mut [Instruction]) {
24,245✔
457
    for i in 0..instructions.len() - 1 {
1,768,290✔
458
        let push = instructions.get(i);
1,744,045✔
459
        let func = instructions.get(i + 1);
1,744,045✔
460

461
        if let (
462
            Some(Instruction {
463
                op_code: OpCode::PUSH,
464
                ..
465
            }),
466
            Some(Instruction {
467
                op_code: OpCode::FUNC | OpCode::TAILCALL,
468
                contents:
469
                    Some(Expr::Atom(RawSyntaxObject {
470
                        ty: TokenType::Identifier(ident),
301,059✔
471
                        ..
301,059✔
472
                    })),
473
                payload_size,
301,059✔
474
                ..
475
            }),
476
        ) = (push, func)
1,744,045✔
477
        {
478
            let payload_size = payload_size.to_u32();
301,059✔
479

480
            let replaced = match *ident {
601,077✔
481
                x if x == *PRIM_PLUS && payload_size == 2 => Some(OpCode::BINOPADD),
301,195✔
482
                x if x == *PRIM_PLUS && payload_size > 0 => Some(OpCode::ADD),
301,035✔
483
                // x if x == *PRIM_MINUS && *payload_size == 2 => Some(OpCode::BINOPSUB),
484
                x if x == *PRIM_MINUS && payload_size > 0 => Some(OpCode::SUB),
301,031✔
485
                x if x == *PRIM_DIV && payload_size > 0 => Some(OpCode::DIV),
301,012✔
486
                x if x == *PRIM_STAR && payload_size > 0 => Some(OpCode::MUL),
301,025✔
487
                x if x == *PRIM_NUM_EQUAL && payload_size == 2 => Some(OpCode::NUMEQUAL),
303,647✔
488
                x if x == *PRIM_EQUAL && payload_size > 0 => Some(OpCode::EQUAL),
300,374✔
489
                x if x == *PRIM_LTE && payload_size > 0 => Some(OpCode::LTE),
300,021✔
490
                _ => None,
300,017✔
491
            };
492

493
            if let Some(new_op_code) = replaced {
1,042✔
494
                // let payload_size = *payload_size;
495
                if let Some(x) = instructions.get_mut(i) {
1,042✔
496
                    x.op_code = new_op_code;
497
                    x.payload_size = u24::from_u32(payload_size);
498
                }
499

500
                if let Some(x) = instructions.get_mut(i + 1) {
1,042✔
501
                    x.op_code = OpCode::PASS;
502
                }
503
            }
504
        }
505
    }
506
}
507

508
pub const fn sequence_to_opcode(pattern: &[(OpCode, usize)]) -> &'static [steel_gen::Pattern] {
×
509
    match pattern {
×
510
        &[(OpCode::MOVEREADLOCAL, _)] => &[steel_gen::Pattern::Single(OpCode::MOVEREADLOCAL)],
×
511
        _ => todo!(),
512
    }
513
}
514

515
#[allow(unused)]
516
pub fn tile_super_instructions(instructions: &mut [Instruction]) {
24,245✔
517
    #[cfg(feature = "dynamic")]
518
    {
519
        pub fn tile<const N: usize>(instructions: &mut [Instruction]) {
24,245✔
520
            // let mut list: List<(usize, OpCode)> = List::new();
521

522
            let mut buffer = [(OpCode::VOID, 0); N];
24,245✔
523

524
            let mut pattern_buffer = Vec::with_capacity(N);
24,245✔
525

526
            // Cell::from_mut()
527

528
            if N > instructions.len() {
24,245✔
529
                return;
24,245✔
530
            }
531

532
            for i in 0..instructions.len() - N {
24,245✔
533
                for j in 0..N {
24,245✔
534
                    buffer[j] = (
24,245✔
535
                        instructions[i + j].op_code,
24,245✔
536
                        instructions[i + j].payload_size,
24,245✔
537
                    );
538
                }
539

540
                // If this is a candidate to match the pattern, let's try to apply it!
541
                if let Some(op_code) = steel_gen::opcode::sequence_to_opcode(&buffer) {
24,245✔
542
                    // Check if this pattern genuinely matches one of the code gen'd ones
543
                    steel_gen::Pattern::from_opcodes_with_buffer(&buffer, &mut pattern_buffer);
24,245✔
544

545
                    if crate::steel_vm::vm::pattern_exists(&pattern_buffer) {
24,245✔
546
                        // log::debug!(target: "super-instructions", "Applying tiling for: {:?}", op_code);
547

548
                        // println!("Applying tiling for: {:?}", op_code);
549
                        // println!("{:?}", pattern_buffer);
550

551
                        instructions[i].op_code = op_code;
24,245✔
552

553
                        continue;
24,245✔
554
                    }
555
                }
556
            }
557

558
            // for (index, op) in list {
559
            //     instructions[index].op_code = op;
560
            // }
561
        }
562

563
        // Super instruction tiling here!
564

565
        if _TILE_SUPER_INSTRUCTIONS {
24,245✔
566
            tile::<11>(instructions);
24,245✔
567
            tile::<10>(instructions);
24,245✔
568
            tile::<9>(instructions);
24,245✔
569
            tile::<8>(instructions);
24,245✔
570
            tile::<7>(instructions);
24,245✔
571
            tile::<6>(instructions);
24,245✔
572
            tile::<5>(instructions);
24,245✔
573
            tile::<4>(instructions);
24,245✔
574
            tile::<3>(instructions);
24,245✔
575
            tile::<2>(instructions);
24,245✔
576
        }
577
    }
578
}
579

580
pub fn merge_conditions_with_if(instructions: &mut [Instruction]) {
24,245✔
581
    for i in 0..instructions.len() - 1 {
1,768,290✔
582
        let condition = instructions.get(i);
1,744,045✔
583
        let guard = instructions.get(i + 2);
1,744,045✔
584

585
        if let (
1,744,045✔
586
            Some(Instruction {
1,744,045✔
587
                op_code: OpCode::LTEIMMEDIATE,
1,744,045✔
588
                ..
1,744,045✔
589
            }),
590
            Some(Instruction {
1,744,045✔
591
                op_code: OpCode::IF,
1,744,045✔
592
                ..
1,744,045✔
593
            }),
594
        ) = (condition, guard)
1,744,045✔
595
        {
596
            if let Some(x) = instructions.get_mut(i) {
8✔
597
                x.op_code = OpCode::LTEIMMEDIATEIF;
598
            }
599

600
            // let replaced = match *ident {
601
            //     x if x == *PLUS && *payload_size == 2 => Some(OpCode::BINOPADD),
602
            //     x if x == *PLUS => Some(OpCode::ADD),
603
            //     x if x == *MINUS => Some(OpCode::SUB),
604
            //     x if x == *DIV => Some(OpCode::DIV),
605
            //     x if x == *STAR => Some(OpCode::MUL),
606
            //     x if x == *EQUAL => Some(OpCode::EQUAL),
607
            //     x if x == *LTE => Some(OpCode::LTE),
608
            //     _ => None,
609
            // };
610

611
            // if let Some(new_op_code) = replaced {
612
            //     let payload_size = *payload_size;
613
            //     if let Some(x) = instructions.get_mut(i) {
614
            //         x.op_code = new_op_code;
615
            //         x.payload_size = payload_size;
616
            //     }
617

618
            //     if let Some(x) = instructions.get_mut(i + 1) {
619
            //         x.op_code = OpCode::PASS;
620
            //     }
621
            // }
622
        }
623
    }
624
}
625

626
pub struct ProgramBuilder(Vec<Vec<DenseInstruction>>);
627
impl Default for ProgramBuilder {
628
    fn default() -> Self {
×
629
        Self::new()
×
630
    }
631
}
632

633
impl ProgramBuilder {
634
    pub fn new() -> Self {
×
635
        ProgramBuilder(Vec::new())
×
636
    }
637

638
    pub fn push(&mut self, val: Vec<DenseInstruction>) {
×
639
        self.0.push(val);
×
640
    }
641
}
642

643
#[derive(Serialize, Deserialize)]
644
pub struct SerializableProgram {
645
    pub instructions: Vec<Vec<DenseInstruction>>,
646
    pub constant_map: Vec<u8>,
647
}
648

649
impl SerializableProgram {
650
    pub fn write_to_file(&self, filename: &str) -> Result<()> {
×
651
        use std::io::prelude::*;
652

653
        let mut file = File::create(format!("{filename}.txt")).unwrap();
×
654

655
        let buffer = bincode::serialize(self).unwrap();
×
656

657
        file.write_all(&buffer)?;
×
658
        Ok(())
×
659
    }
660

661
    pub fn read_from_file(filename: &str) -> Result<Self> {
×
662
        use std::io::prelude::*;
663

664
        let mut file = File::open(format!("{filename}.txt")).unwrap();
×
665

666
        let mut buffer = Vec::new();
×
667

668
        let _ = file.read_to_end(&mut buffer).unwrap();
×
669

670
        let program: SerializableProgram = bincode::deserialize(&buffer).unwrap();
×
671

672
        Ok(program)
×
673
    }
674

675
    pub fn into_program(self) -> Program {
×
676
        let constant_map = ConstantMap::from_bytes(&self.constant_map).unwrap();
×
677
        Program {
678
            constant_map,
679
            instructions: self.instructions,
×
680
            ast: HashMap::new(),
×
681
        }
682
    }
683
}
684

685
/// Represents a Steel program
686
/// The program holds the instructions and the constant map, serialized to bytes
687
pub struct Program {
688
    pub instructions: Vec<Vec<DenseInstruction>>,
689
    pub constant_map: ConstantMap,
690
    pub ast: HashMap<usize, ExprKind>,
691
}
692

693
impl Program {
694
    pub fn new(
×
695
        instructions: Vec<Vec<DenseInstruction>>,
696
        constant_map: ConstantMap,
697
        ast: HashMap<usize, ExprKind>,
698
    ) -> Self {
699
        Program {
700
            instructions,
701
            constant_map,
702
            ast,
703
        }
704
    }
705

706
    pub fn into_serializable_program(self) -> Result<SerializableProgram> {
×
707
        Ok(SerializableProgram {
×
708
            instructions: self.instructions,
×
709
            constant_map: self.constant_map.to_bytes()?,
×
710
        })
711
    }
712
}
713

714
// An inspectable program with debug symbols still included on the instructions
715
// ConstantMap needs to get passed in to the run time to execute the program
716
// This way, the VM knows where to look up values
717
#[derive(Clone)]
718
pub struct RawProgramWithSymbols {
719
    pub(crate) instructions: Vec<Vec<Instruction>>,
720
    pub(crate) constant_map: ConstantMap,
721
    version: String, // TODO -> this should be semver
722
}
723

724
#[derive(Serialize, Deserialize)]
725
pub struct SerializableRawProgramWithSymbols {
726
    instructions: Vec<Vec<Instruction>>,
727
    constant_map: Vec<u8>,
728
    version: String,
729
}
730

731
impl SerializableRawProgramWithSymbols {
732
    pub fn write_to_file(&self, filename: &str) -> Result<()> {
×
733
        use std::io::prelude::*;
734

735
        let mut file = File::create(format!("{filename}.txt")).unwrap();
×
736

737
        let buffer = bincode::serialize(self).unwrap();
×
738

739
        file.write_all(&buffer)?;
×
740
        Ok(())
×
741
    }
742

743
    pub fn read_from_file(filename: &str) -> Result<Self> {
×
744
        use std::io::prelude::*;
745

746
        let mut file = File::open(format!("{filename}.txt")).unwrap();
×
747
        let mut buffer = Vec::new();
×
748
        let _ = file.read_to_end(&mut buffer).unwrap();
×
749
        let program: Self = bincode::deserialize(&buffer).unwrap();
×
750

751
        Ok(program)
×
752
    }
753

754
    pub fn into_raw_program(self) -> RawProgramWithSymbols {
×
755
        let constant_map = ConstantMap::from_bytes(&self.constant_map).unwrap();
×
756
        RawProgramWithSymbols {
757
            // struct_functions: self.struct_functions,
758
            instructions: self.instructions,
×
759
            constant_map,
760
            version: self.version,
×
761
        }
762
    }
763
}
764

765
use std::fs::File;
766
use std::io::{self, BufRead};
767
use std::path::Path;
768

769
// The output is wrapped in a Result to allow matching on errors
770
// Returns an Iterator to the Reader of the lines of the file.
771
fn _read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
×
772
where
773
    P: AsRef<Path>,
774
{
775
    let file = File::open(filename)?;
×
776
    Ok(io::BufReader::new(file).lines())
×
777
}
778

779
// trait Profiler {
780
//     #[inline(always)]
781
//     fn process() -> bool;
782

783
//     fn report(&self);
784
// }
785

786
impl RawProgramWithSymbols {
UNCOV
787
    pub fn new(
×
788
        // struct_functions: Vec<StructFuncBuilderConcrete>,
789
        instructions: Vec<Vec<Instruction>>,
790
        constant_map: ConstantMap,
791
        version: String,
792
    ) -> Self {
793
        Self {
794
            // struct_functions,
795
            instructions,
796
            constant_map,
797
            version,
798
        }
799
    }
800

801
    pub fn profile_instructions(&self) {
×
802
        let iter = self
×
803
            .instructions
×
804
            .iter()
805
            .flat_map(|x| x.iter())
×
806
            .filter(|x| !matches!(x.op_code, OpCode::PASS));
×
807

808
        let mut occurrences = HashMap::new();
×
809
        for instr in iter {
×
810
            *occurrences.entry(instr.op_code).or_default() += 1;
×
811
        }
812

813
        let total: usize = occurrences.values().sum();
×
814

815
        let mut counts = occurrences
×
816
            .into_iter()
817
            .map(|x| (x.0, (x.1 as f64 / total as f64) * 100.0))
×
818
            .collect::<Vec<(OpCode, f64)>>();
819

820
        counts.sort_by(|x, y| y.1.partial_cmp(&x.1).unwrap());
×
821

822
        println!("{counts:#?}");
×
823
    }
824

825
    // Definitely can be improved
826
    // pub fn parse_from_self_hosted_file<P>(file: P) -> Result<Self>
827
    // where
828
    //     P: AsRef<Path>,
829
    // {
830
    //     let mut lines = read_lines(file)?;
831

832
    //     // First line should be the constant map label
833
    //     // let constant_map =
834

835
    //     if let Some(constant_map_label) = lines.next() {
836
    //         if constant_map_label? != "'ConstantMap" {
837
    //             stop!(Generic => "Compiled file expected constant map label")
838
    //         }
839
    //     } else {
840
    //         stop!(Generic => "Missing constant map label")
841
    //     }
842

843
    //     // Temportary interner
844
    //     let mut intern = HashMap::new();
845

846
    //     let constant_map = if let Some(constant_map) = lines.next() {
847
    //         let constant_map = constant_map?;
848

849
    //         let constant_map = constant_map
850
    //             .trim_start_matches('[')
851
    //             .trim_end_matches(']')
852
    //             .split(',')
853
    //             .map(|x| {
854
    //                 // Parse the input
855
    //                 let parsed: std::result::Result<Vec<ExprKind>, ParseError> =
856
    //                     Parser::new(&x, &mut intern).collect();
857
    //                 let parsed = parsed?;
858

859
    //                 Ok(SteelVal::try_from(parsed[0].clone()).unwrap())
860
    //             })
861
    //             .collect::<Result<Vec<_>>>()
862
    //             .map(ConstantMap::from_vec)?;
863

864
    //         constant_map
865
    //     } else {
866
    //         stop!(Generic => "Missing constant map")
867
    //     };
868

869
    //     if let Some(instructions_label) = lines.next() {
870
    //         if instructions_label? != "'Instructions" {
871
    //             stop!(Generic => "Compiled file expected instructions label")
872
    //         }
873
    //     } else {
874
    //         stop!(Generic => "Missing instructions label")
875
    //     }
876

877
    //     let mut instruction_set = Vec::new();
878

879
    //     let mut instructions = Vec::new();
880

881
    //     // Skip past the first 'Expression
882
    //     lines.next();
883

884
    //     for instruction_string in lines {
885
    //         let instruction_string = instruction_string?;
886

887
    //         if instruction_string == "'Expression" {
888
    //             // instructions = Vec::new();
889
    //             // if instruction_set.is_empty() {
890
    //             instruction_set.push(instructions);
891
    //             instructions = Vec::new();
892
    //             // }
893

894
    //             continue;
895
    //         }
896

897
    //         let parsed: std::result::Result<Vec<ExprKind>, ParseError> =
898
    //             Parser::new(&instruction_string, &mut intern).collect();
899
    //         let parsed = parsed?;
900

901
    //         let value = SteelVal::try_from(parsed[0].clone()).unwrap();
902

903
    //         if let SteelVal::ListV(v) = value {
904
    //             // Get the op code here
905
    //             let op_code =
906
    //                 OpCode::from_str(v.get(1).unwrap().symbol_or_else(|| unreachable!()).unwrap());
907

908
    //             // Get the payload
909
    //             let payload = v.get(2).unwrap().int_or_else(|| unreachable!()).unwrap() as usize;
910

911
    //             // Get the contents
912
    //             // If I can't parse the object, just move on
913
    //             let contents = ExprKind::try_from(v.get(3).unwrap())
914
    //                 .ok()
915
    //                 .and_then(|x| x.atom_syntax_object().cloned());
916

917
    //             let instruction = Instruction::new_from_parts(op_code, payload, contents);
918

919
    //             instructions.push(instruction)
920
    //         } else {
921
    //             stop!(Generic => "Instruction serialized incorrectly")
922
    //         }
923
    //     }
924

925
    //     instruction_set.push(instructions);
926

927
    //     Ok(Self::new(
928
    //         instruction_set,
929
    //         constant_map,
930
    //         "0.0.1".to_string(),
931
    //     ))
932
    // }
933

934
    pub fn into_serializable_program(self) -> Result<SerializableRawProgramWithSymbols> {
×
935
        Ok(SerializableRawProgramWithSymbols {
×
936
            instructions: self.instructions,
×
937
            constant_map: self.constant_map.to_bytes()?,
×
938
            version: self.version,
×
939
        })
940
    }
941

942
    pub fn debug_print(&self) {
×
943
        self.instructions
×
944
            .iter()
945
            .for_each(|i| println!("{}\n\n", crate::core::instructions::disassemble(i)))
×
946
    }
947

948
    pub fn debug_print_log(&self) {
×
949
        self.instructions
×
950
            .iter()
951
            .for_each(|i| log::info!("{}\n\n", crate::core::instructions::disassemble(i)))
×
952
    }
953

954
    /// Applies a peephole style optimization to the underlying instruction set
955
    pub fn with_optimization<F: Fn(&mut [Instruction])>(&mut self, f: F) {
×
956
        for instructions in &mut self.instructions {
×
957
            f(instructions)
×
958
        }
959
    }
960

961
    // Apply the optimizations to raw bytecode
962
    pub(crate) fn apply_optimizations(&mut self) -> &mut Self {
779✔
963
        // if std::env::var("CODE_GEN_V2").is_err() {
964
        // Run down the optimizations here
965
        for instructions in &mut self.instructions {
73,514✔
966
            inline_num_operations(instructions);
24,245✔
967
            convert_call_globals(instructions);
24,245✔
968

969
            // gimmick_super_instruction(instructions);
970
            // move_read_local_call_global(instructions);
971
            specialize_read_local(instructions);
24,245✔
972

973
            merge_conditions_with_if(instructions);
24,245✔
974

975
            specialize_constants(instructions).unwrap();
24,245✔
976

977
            // Apply the super instruction tiling!
978
            tile_super_instructions(instructions);
24,245✔
979

980
            // specialize_exit_jmp(instructions);
981

982
            // loop_condition_local_const_arity_two(instructions);
983
        }
984

985
        self
779✔
986
    }
987

988
    pub fn debug_generate_instructions(
×
989
        mut self,
990
        symbol_map: &mut SymbolMap,
991
    ) -> Result<Vec<String>> {
992
        let mut interner = DebruijnIndicesInterner::default();
×
993

994
        for expression in &mut self.instructions {
×
995
            interner.collect_first_pass_defines(expression, symbol_map)?
×
996
        }
997

998
        for expression in &mut self.instructions {
×
999
            interner.collect_second_pass_defines(expression, symbol_map)?
×
1000
        }
1001

1002
        // TODO try here - the loop condition local const arity two seems to rely on the
1003
        // existence of having been already adjusted by the interner
1004
        for instructions in &mut self.instructions {
×
1005
            // loop_condition_local_const_arity_two(instructions);
1006
            specialize_constants(instructions)?;
×
1007
        }
1008

1009
        // Put the new struct functions at the front
1010
        // struct_instructions.append(&mut self.instructions);
1011
        // self.instructions = struct_instructions;
1012

1013
        Ok(self
×
1014
            .instructions
×
1015
            .into_iter()
×
1016
            .map(|i| crate::core::instructions::disassemble(&i))
×
1017
            .collect())
×
1018
    }
1019

1020
    pub fn debug_build(mut self, _name: String, symbol_map: &mut SymbolMap) -> Result<()> {
×
1021
        #[cfg(feature = "profiling")]
1022
        let now = Instant::now();
×
1023

1024
        // let mut struct_instructions = Vec::new();
1025

1026
        // for builder in &self.struct_functions {
1027
        //     // Add the eventual function names to the symbol map
1028
        //     let indices = symbol_map.insert_struct_function_names_from_concrete(builder);
1029

1030
        //     // Get the value we're going to add to the constant map for eventual use
1031
        //     // Throw the bindings in as well
1032
        //     let constant_values = builder.to_constant_val(indices);
1033
        //     let idx = self.constant_map.add_or_get(constant_values);
1034

1035
        //     struct_instructions.push(vec![Instruction::new_struct(idx), Instruction::new_pop()]);
1036
        // }
1037

1038
        let mut interner = DebruijnIndicesInterner::default();
×
1039

1040
        for expression in &mut self.instructions {
×
1041
            interner.collect_first_pass_defines(expression, symbol_map)?
×
1042
        }
1043

1044
        for expression in &mut self.instructions {
×
1045
            interner.collect_second_pass_defines(expression, symbol_map)?
×
1046
        }
1047

1048
        // TODO try here - the loop condition local const arity two seems to rely on the
1049
        // existence of having been already adjusted by the interner
1050
        for instructions in &mut self.instructions {
×
1051
            // loop_condition_local_const_arity_two(instructions);
1052
            specialize_constants(instructions)?;
×
1053
        }
1054

1055
        // Put the new struct functions at the front
1056
        // struct_instructions.append(&mut self.instructions);
1057
        // self.instructions = struct_instructions;
1058

1059
        self.instructions
×
1060
            .iter()
1061
            .for_each(|i| println!("{}\n\n", crate::core::instructions::disassemble(i)));
×
1062

1063
        #[cfg(feature = "profiling")]
×
1064
        if log_enabled!(target: "pipeline_time", log::Level::Debug) {
×
1065
            debug!(target: "pipeline_time", "Executable Build Time: {:?}", now.elapsed());
×
1066
        }
1067

1068
        // let mut sorted_symbol_map = symbol_map.map.iter().collect::<Vec<_>>();
1069
        // sorted_symbol_map.sort_by_key(|x| x.1);
1070

1071
        // println!("Symbol Map: {:#?}", sorted_symbol_map);
1072

1073
        Ok(())
×
1074
    }
1075

1076
    // TODO -> check out the spans part of this
1077
    // also look into having the constant map be correct mapping
1078
    // I think the run time will have to swap the constant map in and out
1079
    pub fn build(mut self, name: String, symbol_map: &mut SymbolMap) -> Result<Executable> {
778✔
1080
        #[cfg(feature = "profiling")]
1081
        let now = Instant::now();
778✔
1082

1083
        let mut interner = DebruijnIndicesInterner::default();
778✔
1084

1085
        for expression in &mut self.instructions {
49,240✔
1086
            interner.collect_first_pass_defines(expression, symbol_map)?
×
1087
        }
1088

1089
        for expression in &mut self.instructions {
49,210✔
1090
            interner.collect_second_pass_defines(expression, symbol_map)?
22✔
1091
        }
1092

1093
        // if std::env::var("CODE_GEN_V2").is_err() {
1094
        // TODO try here - the loop condition local const arity two seems to rely on the
1095
        // existence of having been already adjusted by the interner
1096
        for instructions in &mut self.instructions {
49,146✔
1097
            // TODO: Re-enable optimizations
1098
            // loop_condition_local_const_arity_two(instructions);
1099
            specialize_constants(instructions)?;
×
1100
            // gimmick_super_instruction(instructions);
1101
            // move_read_local_call_global(instructions);
1102
            specialize_read_local(instructions);
24,195✔
1103
        }
1104
        // }
1105

1106
        let (spans, instructions) = extract_spans(self.instructions);
756✔
1107

1108
        // let mut sorted_symbol_map = symbol_map.map.iter().collect::<Vec<_>>();
1109
        // sorted_symbol_map.sort_by_key(|x| x.1);
1110

1111
        // println!("Symbol Map: {:#?}", sorted_symbol_map);
1112

1113
        #[cfg(feature = "profiling")]
756✔
1114
        if log_enabled!(target: "pipeline_time", log::Level::Debug) {
756✔
1115
            debug!(target: "pipeline_time", "Executable Build Time: {:?}", now.elapsed());
756✔
1116
        }
1117

1118
        Ok(Executable {
756✔
1119
            name: Shared::new(name),
756✔
1120
            version: Shared::new(self.version),
756✔
1121
            #[cfg(not(target_arch = "wasm32"))]
756✔
1122
            time_stamp: Some(SystemTime::now()),
756✔
1123
            #[cfg(target_arch = "wasm32")]
756✔
1124
            time_stamp: None,
756✔
1125
            instructions: instructions
756✔
1126
                .into_iter()
756✔
1127
                .map(|x| Shared::from(x.into_boxed_slice()))
25,707✔
1128
                .collect(),
756✔
1129
            constant_map: self.constant_map,
756✔
1130
            spans,
756✔
1131
        })
1132
    }
1133
}
1134

1135
// TODO -> replace spans on instructions with index into span vector
1136
// this is kinda nasty but it _should_ work
1137
fn extract_spans(
756✔
1138
    instructions: Vec<Vec<Instruction>>,
1139
) -> (Vec<Shared<[Span]>>, Vec<Vec<DenseInstruction>>) {
1140
    // let mut span_vec = Vec::with_capacity(instructions.iter().map(|x| x.len()).sum());
1141

1142
    // for instruction_set in &instructions {
1143
    //     for instruction in instruction_set {
1144
    //         if let Some(syn) = &instruction.contents {
1145
    //             span_vec.push(syn.span)
1146
    //         } else {
1147
    //             span_vec.push(Span::default())
1148
    //         }
1149
    //     }
1150
    // }
1151

1152
    let span_vec = instructions
756✔
1153
        .iter()
1154
        .map(|x| {
24,951✔
1155
            x.iter()
24,195✔
1156
                .map(|x| {
1,787,254✔
1157
                    x.contents
1,763,059✔
1158
                        .as_ref()
1,763,059✔
1159
                        .map(|x| match x {
3,036,312✔
1160
                            Expr::Atom(a) => a.span,
1,141,858✔
1161
                            Expr::List(l) => get_span(l),
131,395✔
1162
                        })
1163
                        .unwrap_or_default()
1,763,059✔
1164
                })
1165
                .collect()
24,195✔
1166
        })
1167
        .collect();
1168

1169
    let instructions: Vec<_> = instructions
756✔
1170
        .into_iter()
1171
        .map(|x| {
24,951✔
1172
            // let len = x.len();
1173
            x.into_iter()
24,195✔
1174
                .map(|i| {
1,787,254✔
1175
                    DenseInstruction::new(
1,763,059✔
1176
                        i.op_code,
1,763,059✔
1177
                        i.payload_size.try_into().unwrap_or_else(|_| {
1,763,059✔
1178
                            println!("{:?}", i);
×
1179
                            panic!("Unable to lower instruction to bytecode!")
×
1180
                        }),
1181
                    )
1182
                })
1183
                .collect()
24,195✔
1184
        })
1185
        .collect();
1186

1187
    (span_vec, instructions)
756✔
1188
}
1189

1190
// A program stripped of its debug symbols, but only constructable by running a pass
1191
// over it with the symbol map to intern all of the symbols in the order they occurred
1192
#[allow(unused)]
1193
#[derive(Clone)]
1194
pub struct Executable {
1195
    pub(crate) name: Shared<String>,
1196
    pub(crate) version: Shared<String>,
1197
    pub(crate) time_stamp: Option<SystemTime>, // TODO -> don't use system time, probably not as portable, prefer date time
1198
    pub(crate) instructions: Vec<Shared<[DenseInstruction]>>,
1199
    pub(crate) constant_map: ConstantMap,
1200
    pub(crate) spans: Vec<Shared<[Span]>>,
1201
}
1202

1203
impl Executable {
1204
    pub fn name(&self) -> &str {
×
1205
        &self.name
×
1206
    }
1207

1208
    pub fn time_stamp(&self) -> &Option<SystemTime> {
×
1209
        &self.time_stamp
×
1210
    }
1211
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc