fortrangoingonforty/armfortas / b647206

Browse files

Lower COUNT(mask, dim) to per-slice descriptor result

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
b6472063c8f30bcf6c5dd915119558d3c630855e
Parents
5434355
Tree
7a42573

2 changed files

StatusFile+-
M src/ir/lower/core.rs 121 0
M src/ir/lower/stmt.rs 17 0
src/ir/lower/core.rsmodified
@@ -7314,6 +7314,26 @@ pub(super) fn lower_logical_reduction_intrinsic_ast(
73147314
         return None;
73157315
     }
73167316
 
7317
+    // F2018 §16.9.46: COUNT(MASK, DIM=k) returns an integer ARRAY of
7318
+    // rank N-1, not a scalar. The scalar runtime helper below would
7319
+    // collapse the entire mask into one i32 — when assigned into a
7320
+    // rank-1 destination (e.g. `n = count(mask, 1)` in stdlib_stats
7321
+    // var_mask_2), the integer count would then be passed as the
7322
+    // source descriptor pointer to afs_assign_allocatable, dereferencing
7323
+    // a tiny address (e.g. 0x3 for count=3) and aborting. Bail only
7324
+    // when an actual dim= argument is present; `count(mask, kind=int64)`
7325
+    // is still a scalar reduction (kind is just the result kind) and
7326
+    // must keep using the scalar path.
7327
+    if name == "count" {
7328
+        let has_dim = args.iter().enumerate().any(|(i, a)| {
7329
+            let kw = a.keyword.as_deref().map(|s| s.to_lowercase());
7330
+            matches!(kw.as_deref(), Some("dim")) || (i == 1 && kw.is_none())
7331
+        });
7332
+        if has_dim {
7333
+            return None;
7334
+        }
7335
+    }
7336
+
73177337
     let arg0 = args.first().and_then(|arg| {
73187338
         if let SectionSubscript::Element(expr) = &arg.value {
73197339
             Some(expr)
@@ -23455,6 +23475,81 @@ pub(super) fn lower_transfer_array_expr_descriptor(
2345523475
 /// rank-(N-1) descriptor. Returns None when DIM is absent (scalar
2345623476
 /// SUM(ARRAY) lives in the standard intrinsic path) or when the
2345723477
 /// optional MASK is supplied (no mask runtime support yet).
23478
+/// F2018 §16.9.46 COUNT(MASK, DIM=k) returns an integer array of rank
23479
+/// N-1 with per-slice true-element counts along dimension k. The
23480
+/// scalar logical-reduction path lowers `count` to `afs_array_count_logical`,
23481
+/// which returns a single i32 — with DIM= present that's incorrect: the
23482
+/// stdlib_stats `var_mask_2_*` body assigns the result into a real
23483
+/// rank-1 array `n`, and the compiler then passed the scalar count as
23484
+/// the source descriptor pointer to afs_assign_allocatable, crashing
23485
+/// with a misaligned-pointer dereference at address 0x3 (the count
23486
+/// value masquerading as a pointer).
23487
+pub(super) fn lower_array_count_dim_descriptor(
23488
+    b: &mut FuncBuilder,
23489
+    locals: &HashMap<String, LocalInfo>,
23490
+    args: &[crate::ast::expr::Argument],
23491
+    st: &SymbolTable,
23492
+    type_layouts: Option<&crate::sema::type_layout::TypeLayoutRegistry>,
23493
+    internal_funcs: Option<&HashMap<String, u32>>,
23494
+    contained_host_refs: Option<&HashMap<String, Vec<String>>>,
23495
+    descriptor_params: Option<&HashMap<String, Vec<bool>>>,
23496
+) -> Option<(ValueId, IrType)> {
23497
+    use crate::ast::expr::SectionSubscript;
23498
+    if args.is_empty() {
23499
+        return None;
23500
+    }
23501
+    let SectionSubscript::Element(mask_expr) = &args[0].value else {
23502
+        return None;
23503
+    };
23504
+    let mut dim_expr: Option<&crate::ast::expr::SpannedExpr> = None;
23505
+    for (i, arg) in args.iter().enumerate() {
23506
+        let kw = arg.keyword.as_deref().map(|s| s.to_lowercase());
23507
+        match (i, kw.as_deref()) {
23508
+            (1, None) | (_, Some("dim")) => {
23509
+                if let SectionSubscript::Element(e) = &arg.value {
23510
+                    dim_expr = Some(e);
23511
+                }
23512
+            }
23513
+            _ => {}
23514
+        }
23515
+    }
23516
+    let dim_expr = dim_expr?;
23517
+
23518
+    let (mask_desc, _) = lower_array_expr_descriptor(
23519
+        b,
23520
+        locals,
23521
+        mask_expr,
23522
+        st,
23523
+        type_layouts,
23524
+        internal_funcs,
23525
+        contained_host_refs,
23526
+        descriptor_params,
23527
+    )?;
23528
+
23529
+    let dim_raw = super::expr::lower_expr_with_optional_layouts(b, locals, dim_expr, st, type_layouts);
23530
+    let dim_val = match b.func().value_type(dim_raw) {
23531
+        Some(IrType::Int(IntWidth::I32)) => dim_raw,
23532
+        Some(IrType::Int(_)) => b.int_trunc(dim_raw, IntWidth::I32),
23533
+        _ => dim_raw,
23534
+    };
23535
+
23536
+    let result_desc = b.alloca(IrType::Array(Box::new(IrType::Int(IntWidth::I8)), 384));
23537
+    let zero_i32 = b.const_i32(0);
23538
+    let sz384 = b.const_i64(384);
23539
+    b.call(
23540
+        FuncRef::External("memset".into()),
23541
+        vec![result_desc, zero_i32, sz384],
23542
+        IrType::Ptr(Box::new(IrType::Int(IntWidth::I8))),
23543
+    );
23544
+
23545
+    b.call(
23546
+        FuncRef::External("afs_array_count_logical_dim".into()),
23547
+        vec![mask_desc, dim_val, result_desc],
23548
+        IrType::Void,
23549
+    );
23550
+    Some((result_desc, IrType::Int(IntWidth::I32)))
23551
+}
23552
+
2345823553
 pub(super) fn lower_array_sum_dim_descriptor(
2345923554
     b: &mut FuncBuilder,
2346023555
     locals: &HashMap<String, LocalInfo>,
@@ -25504,6 +25599,32 @@ pub(super) fn lower_array_expr_descriptor(
2550425599
                         return Some(result);
2550525600
                     }
2550625601
                 }
25602
+                // F2018 §16.9.46: COUNT(MASK, DIM=k) returns an integer
25603
+                // array of rank N-1. Without this dim-aware path the
25604
+                // scalar logical-reduction lowering returns a single i32
25605
+                // and the caller copies that into the destination array
25606
+                // descriptor's source slot — afs_assign_allocatable then
25607
+                // dereferences the count value as a pointer and aborts.
25608
+                if name.eq_ignore_ascii_case("count") {
25609
+                    let has_dim = args.iter().enumerate().any(|(i, a)| {
25610
+                        let kw = a.keyword.as_deref().map(|s| s.to_lowercase());
25611
+                        matches!(kw.as_deref(), Some("dim")) || (i == 1 && kw.is_none())
25612
+                    });
25613
+                    if has_dim {
25614
+                        if let Some(result) = lower_array_count_dim_descriptor(
25615
+                            b,
25616
+                            locals,
25617
+                            args,
25618
+                            st,
25619
+                            type_layouts,
25620
+                            internal_funcs,
25621
+                            contained_host_refs,
25622
+                            descriptor_params,
25623
+                        ) {
25624
+                            return Some(result);
25625
+                        }
25626
+                    }
25627
+                }
2550725628
                 // F2018 §16.9.135: MERGE is elemental. When at least one of the
2550825629
                 // first two actuals (or the mask) is an array, the result is
2550925630
                 // an array; we materialize it via per-element select, leaving
src/ir/lower/stmt.rsmodified
@@ -390,6 +390,23 @@ pub(crate) fn lower_stmt(b: &mut FuncBuilder, ctx: &mut LowerCtx, stmt: &Spanned
390390
                                                             || (i == 1 && kw.is_none())
391391
                                                     })
392392
                                                 )
393
+                                                || (
394
+                                                    // count(mask, dim) is rank-N-1 integer
395
+                                                    // array: same routing as sum(arr, dim).
396
+                                                    // Without this, the scalar logical-
397
+                                                    // reduction path returns a single i32
398
+                                                    // total and the array-assign treats it
399
+                                                    // as a source descriptor, dereferencing
400
+                                                    // a tiny address (e.g. 0x3) and aborting
401
+                                                    // in afs_assign_allocatable. Surfaced
402
+                                                    // in stdlib_stats var_mask_2_*.
403
+                                                    lname == "count"
404
+                                                    && call_args.iter().enumerate().any(|(i, a)| {
405
+                                                        let kw = a.keyword.as_deref().map(|s| s.to_lowercase());
406
+                                                        matches!(kw.as_deref(), Some("dim"))
407
+                                                            || (i == 1 && kw.is_none())
408
+                                                    })
409
+                                                )
393410
                                             } else {
394411
                                                 false
395412
                                             };