Skip to content

Commit 9af5e14

Browse files
committed
Preserve logical storage through diag()
1 parent ab11aa0 commit 9af5e14

4 files changed

Lines changed: 231 additions & 17 deletions

File tree

R/r2f-matrix-blas.R

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -262,14 +262,22 @@ can_use_output <- function(
262262
expected_dims = NULL,
263263
context,
264264
allow_alias = character(),
265-
mode = "double"
265+
mode = "double",
266+
logical_is_c_int = FALSE
266267
) {
268+
stopifnot(
269+
is_bool(logical_is_c_int),
270+
!logical_is_c_int || identical(mode, "logical")
271+
)
267272
if (is.null(dest)) {
268273
return(FALSE)
269274
}
270275
if (!identical(dest@mode, mode)) {
271276
return(FALSE)
272277
}
278+
if (!identical(logical_as_int(dest), logical_is_c_int)) {
279+
return(FALSE)
280+
}
273281
assert_dest_dims_compatible(dest, expected_dims, context)
274282
output_name <- dest@name
275283
if (is.null(output_name) || !nzchar(output_name)) {
@@ -293,7 +301,8 @@ ensure_blas_operand_name <- function(x, hoist) {
293301
}
294302
tmp <- hoist$declare_tmp(
295303
mode = x@value@mode %||% "double",
296-
dims = x@value@dims
304+
dims = x@value@dims,
305+
logical_as_int = logical_as_int(x@value)
297306
)
298307
hoist$emit(glue("{tmp@name} = {x}"))
299308
tmp@name
@@ -1199,6 +1208,7 @@ diag_extract <- function(x, scope, hoist, dest = NULL, context = "diag") {
11991208
diag_len <- diag_length_expr(x_dims$rows, x_dims$cols, context)
12001209

12011210
x_name <- ensure_blas_operand_name(x, hoist)
1211+
logical_is_c_int <- logical_as_int(x@value)
12021212

12031213
writes_to_dest <- FALSE
12041214
if (
@@ -1207,14 +1217,19 @@ diag_extract <- function(x, scope, hoist, dest = NULL, context = "diag") {
12071217
input_names = x_name,
12081218
expected_dims = list(diag_len),
12091219
context = context,
1210-
mode = x@value@mode
1220+
mode = x@value@mode,
1221+
logical_is_c_int = logical_is_c_int
12111222
)
12121223
) {
12131224
out_var <- dest
12141225
out_name <- dest@name
12151226
writes_to_dest <- TRUE
12161227
} else {
1217-
out_var <- hoist$declare_tmp(mode = x@value@mode, dims = list(diag_len))
1228+
out_var <- hoist$declare_tmp(
1229+
mode = x@value@mode,
1230+
dims = list(diag_len),
1231+
logical_as_int = logical_is_c_int
1232+
)
12181233
out_name <- out_var@name
12191234
}
12201235

@@ -1249,14 +1264,7 @@ diag_matrix <- function(
12491264
assert_rank_leq1(x, paste0(context, " expects a vector or scalar input"))
12501265

12511266
mode <- x@value@mode
1252-
zero <- switch(
1253-
mode,
1254-
double = "0.0_c_double",
1255-
integer = "0_c_int",
1256-
logical = ".false.",
1257-
complex = "(0.0_c_double, 0.0_c_double)",
1258-
stop(context, " does not support mode ", mode, call. = FALSE)
1259-
)
1267+
logical_is_c_int <- logical_as_int(x@value)
12601268

12611269
diag_len <- diag_length_expr(nrow, ncol, context)
12621270
x_scalar <- passes_as_scalar(x@value)
@@ -1271,17 +1279,30 @@ diag_matrix <- function(
12711279
input_names = x_name,
12721280
expected_dims = list(nrow, ncol),
12731281
context = context,
1274-
mode = mode
1282+
mode = mode,
1283+
logical_is_c_int = logical_is_c_int
12751284
)
12761285
) {
12771286
out_var <- dest
12781287
out_name <- dest@name
12791288
writes_to_dest <- TRUE
12801289
} else {
1281-
out_var <- hoist$declare_tmp(mode = mode, dims = list(nrow, ncol))
1290+
out_var <- hoist$declare_tmp(
1291+
mode = mode,
1292+
dims = list(nrow, ncol),
1293+
logical_as_int = logical_is_c_int
1294+
)
12821295
out_name <- out_var@name
12831296
}
12841297

1298+
zero <- switch(
1299+
mode,
1300+
double = "0.0_c_double",
1301+
integer = "0_c_int",
1302+
logical = if (logical_as_int(out_var)) "0_c_int" else ".false.",
1303+
complex = "(0.0_c_double, 0.0_c_double)",
1304+
stop(context, " does not support mode ", mode, call. = FALSE)
1305+
)
12851306
hoist$emit(glue("{out_name} = {zero}"))
12861307

12871308
idx_i <- hoist$declare_tmp(mode = "integer", dims = NULL)

R/r2f-matrix-infer.R

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,11 @@ infer_dest_diag <- function(args, scope) {
318318
diag_len <- diag_length_expr(x_dims$rows, x_dims$cols, "diag")
319319
# diag_extract() preserves x's mode; a double-inferred dest would
320320
# mislabel an integer diagonal.
321-
return(Variable(x@mode, list(diag_len)))
321+
return(Variable(
322+
mode = x@mode,
323+
dims = list(diag_len),
324+
logical_as_int = logical_as_int(x)
325+
))
322326
}
323327

324328
# Case: x is a scalar literal (identity matrix of that size)
@@ -349,12 +353,20 @@ infer_dest_diag <- function(args, scope) {
349353
if (is.null(ncol)) {
350354
ncol <- nrow
351355
}
352-
return(Variable(x@mode, list(nrow, ncol)))
356+
return(Variable(
357+
mode = x@mode,
358+
dims = list(nrow, ncol),
359+
logical_as_int = logical_as_int(x)
360+
))
353361
}
354362
# No nrow/ncol: square matrix from vector length
355363
if (x@rank == 1L) {
356364
len <- var_dim_or_one(x, 1L)
357-
return(Variable(x@mode, list(len, len)))
365+
return(Variable(
366+
mode = x@mode,
367+
dims = list(len, len),
368+
logical_as_int = logical_as_int(x)
369+
))
358370
}
359371
}
360372

tests/testthat/_snaps/matrix.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,156 @@
307307
return out;
308308
}
309309

310+
# diag() preserves integer-backed logical storage in an intermediate
311+
312+
Code
313+
fn
314+
Output
315+
function(m) {
316+
declare(type(m = logical(2, 2)))
317+
d <- diag(m)
318+
as.integer(d)
319+
}
320+
<environment: 0x0>
321+
Code
322+
cat(fsub)
323+
Output
324+
subroutine fn(m, out_) bind(c)
325+
use iso_c_binding, only: c_int
326+
implicit none
327+
328+
! manifest start
329+
! args
330+
integer(c_int), intent(in) :: m(2, 2) ! logical
331+
integer(c_int), intent(out) :: out_(2)
332+
333+
! locals
334+
integer(c_int) :: d(2) ! logical
335+
! manifest end
336+
337+
338+
block
339+
integer(c_int) :: btmp1_
340+
341+
do btmp1_ = 1_c_int, int(2, kind=c_int)
342+
d(btmp1_) = m(btmp1_, btmp1_)
343+
end do
344+
end block
345+
out_ = d
346+
end subroutine
347+
Code
348+
cat(cwrapper)
349+
Output
350+
#define R_NO_REMAP
351+
#include <R.h>
352+
#include <Rinternals.h>
353+
354+
355+
extern void fn(const int* const m__, int* const out___);
356+
357+
SEXP fn_(SEXP _args) {
358+
// m
359+
_args = CDR(_args);
360+
SEXP m = CAR(_args);
361+
if (TYPEOF(m) != LGLSXP) {
362+
Rf_error("typeof(m) must be 'logical', not '%s'", Rf_type2char(TYPEOF(m)));
363+
}
364+
const int* const m__ = LOGICAL(m);
365+
const int* const m__dim_ = ({
366+
SEXP dim_ = Rf_getAttrib(m, R_DimSymbol);
367+
if (Rf_length(dim_) != 2) Rf_error(
368+
"m must be a 2D-array, but length(dim(m)) is %i",
369+
(int) Rf_length(dim_));
370+
INTEGER(dim_);});
371+
const int m__dim_1_ = m__dim_[0];
372+
const int m__dim_2_ = m__dim_[1];
373+
374+
if (m__dim_1_ != 2)
375+
Rf_error("dim(m)[1] must be 2, not %0.f",
376+
(double)m__dim_1_);
377+
if (m__dim_2_ != 2)
378+
Rf_error("dim(m)[2] must be 2, not %0.f",
379+
(double)m__dim_2_);
380+
const R_xlen_t out___len_ = 2;
381+
SEXP out_ = PROTECT(Rf_allocVector(INTSXP, out___len_));
382+
int* out___ = INTEGER(out_);
383+
384+
fn(m__, out___);
385+
386+
UNPROTECT(1);
387+
return out_;
388+
}
389+
390+
# diag() initializes integer-backed logical outputs as integers
391+
392+
Code
393+
fn
394+
Output
395+
function(x) {
396+
declare(type(x = logical(2)))
397+
diag(x, 3L, 4L)
398+
}
399+
<environment: 0x0>
400+
Code
401+
cat(fsub)
402+
Output
403+
subroutine fn(x, out_) bind(c)
404+
use iso_c_binding, only: c_int
405+
implicit none
406+
407+
! manifest start
408+
! args
409+
integer(c_int), intent(in) :: x(2) ! logical
410+
integer(c_int), intent(out) :: out_(3, 4) ! logical
411+
! manifest end
412+
413+
414+
block
415+
integer(c_int) :: btmp1_
416+
417+
out_ = 0_c_int
418+
do btmp1_ = 1_c_int, int(3, kind=c_int)
419+
out_(btmp1_, btmp1_) = x(1_c_int + mod(btmp1_ - 1_c_int, int(2, kind=c_int)))
420+
end do
421+
end block
422+
end subroutine
423+
Code
424+
cat(cwrapper)
425+
Output
426+
#define R_NO_REMAP
427+
#include <R.h>
428+
#include <Rinternals.h>
429+
430+
431+
extern void fn(const int* const x__, int* const out___);
432+
433+
SEXP fn_(SEXP _args) {
434+
// x
435+
_args = CDR(_args);
436+
SEXP x = CAR(_args);
437+
if (TYPEOF(x) != LGLSXP) {
438+
Rf_error("typeof(x) must be 'logical', not '%s'", Rf_type2char(TYPEOF(x)));
439+
}
440+
const int* const x__ = LOGICAL(x);
441+
const R_xlen_t x__len_ = Rf_xlength(x);
442+
443+
if (x__len_ != 2)
444+
Rf_error("length(x) must be 2, not %0.f",
445+
(double)x__len_);
446+
const R_xlen_t out___len_ = (3) * (4);
447+
SEXP out_ = PROTECT(Rf_allocVector(LGLSXP, out___len_));
448+
int* out___ = LOGICAL(out_);
449+
{
450+
const SEXP _dim_sexp = PROTECT(Rf_allocVector(INTSXP, 2));
451+
int* const _dim = INTEGER(_dim_sexp);
452+
_dim[0] = 3;
453+
_dim[1] = 4;
454+
Rf_dimgets(out_, _dim_sexp);
455+
}
456+
457+
fn(x__, out___);
458+
459+
UNPROTECT(2);
460+
return out_;
461+
}
462+

tests/testthat/test-matrix.R

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,3 +363,31 @@ test_that("t() and diag() preserve logical mode", {
363363
}
364364
expect_quick_equal(dfn, list(m))
365365
})
366+
367+
test_that("diag() preserves integer-backed logical storage in an intermediate", {
368+
fn <- function(m) {
369+
declare(type(m = logical(2, 2)))
370+
d <- diag(m)
371+
as.integer(d)
372+
}
373+
374+
m <- matrix(c(TRUE, FALSE, TRUE, FALSE), 2, 2)
375+
expect_match(
376+
as.character(r2f(fn)),
377+
"integer(c_int) :: d(2)",
378+
fixed = TRUE
379+
)
380+
expect_translation_snapshots(fn)
381+
expect_quick_identical(fn, list(m))
382+
})
383+
384+
test_that("diag() initializes integer-backed logical outputs as integers", {
385+
fn <- function(x) {
386+
declare(type(x = logical(2)))
387+
diag(x, 3L, 4L)
388+
}
389+
390+
expect_match(as.character(r2f(fn)), "out_ = 0_c_int", fixed = TRUE)
391+
expect_translation_snapshots(fn)
392+
expect_quick_identical(fn, list(c(TRUE, FALSE)))
393+
})

0 commit comments

Comments
 (0)