* fixnum? VM primitive, increasing fixnum operation speed @ 2011-03-23 2:19 Andreas Rottmann 2011-03-23 2:19 ` [PATCH] Add `fixnum?' VM primitive Andreas Rottmann 0 siblings, 1 reply; 12+ messages in thread From: Andreas Rottmann @ 2011-03-23 2:19 UTC (permalink / raw) To: guile-devel This is another piece of my attempt at getting more speed out of the `(rnrs arithmetic fixnums)' library. The idea is that this patch would be applied to master. I think, at least in the current form, it is not eligible for 2.0.x, as it messes up the VM opcode numbers, which breaks all .go files, IIUC. After this patch has been applied, the R6RS fixnums library can be adapted to use the new primitive. I have locally already tried this, and on top of the previous patch ("Take some lowhanging fruit to speed up R6RS fixnum operations"), this one speeds up the ZIP benchmark by another factor of 1.75, yielding a runtime of 15 seconds. The fxxor benchmark added by the previous patch gets a ~3x speed boost. Now, I'm still not entirely satisfied, but these two patches should cover the lowest hanging fruits without changing the semantics of the fixnum operations. For well-behaved programs, it would not be necessary to do the checks for fixnum-ness at all, neither for the arguments nor for the return value. This would in effect alias the fixnum operations to their non-fixnum counterparts, violating R6RS, which demands exceptions to be thrown if arguments or return values exceed fixnum range. I conjecture that this would give another noticable boost, as it would eliminate at least a procedure call and the `fixnum?' checks. I'd argue that deviating from R6RS in this area is not desirable; after all I think the intention of the R6RS fixnum library is to provide specialized operations so that implementations can provide fast-path versions of the more general procedures when it is known that fixnum range suffices -- the fact that fixnum arithmetic is actually *slower* than general arithmetic on Guile is not a given, but an artefact of the way they are currently implemented. Ideally (at least speed-wise), all of the fixnum operations should be VM primitives. I'm not sure if we have enough opcode space for that. It also raises the question of maintainablity (which probably can be kept in check with appropriate C preprocessor trickery), and how to deal with the exceptions these procedures are required to throw. As we probably don't want to tie the R6RS exception system directly into VM, I was thinking of using a fluid containing a procedure that does the actual exception throwing; that fluid could be set by some R6RS library. Well, this is getting quite long already, so I'll stop for now. I'm looking forward to any responses! ,,rotty ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH] Add `fixnum?' VM primitive 2011-03-23 2:19 fixnum? VM primitive, increasing fixnum operation speed Andreas Rottmann @ 2011-03-23 2:19 ` Andreas Rottmann 2011-03-24 22:01 ` Ludovic Courtès 0 siblings, 1 reply; 12+ messages in thread From: Andreas Rottmann @ 2011-03-23 2:19 UTC (permalink / raw) To: guile-devel This primitive can be used to significantly speed up the operations in `(rnrs arithmetic fixnums)'. * libguile/numbers.c (scm_fixnum_p): New predicate. * libguile/numbers.h: Add prototype. * libguile/vm-i-scheme.c (fixnump): New VM primitive; renumbered subsequent operations. * module/language/tree-il/compile-glil.scm (*primcall-ops*): Add `fixnum?'. * module/language/tree-il/primitives.scm (*interesting-primitive-names*): Add `fixnum?'. --- libguile/numbers.c | 9 ++ libguile/numbers.h | 1 + libguile/vm-i-scheme.c | 148 +++++++++++++++-------------- module/language/tree-il/compile-glil.scm | 3 +- module/language/tree-il/primitives.scm | 4 +- 5 files changed, 91 insertions(+), 74 deletions(-) diff --git a/libguile/numbers.c b/libguile/numbers.c index b8cfa5d..5d42fbf 100644 --- a/libguile/numbers.c +++ b/libguile/numbers.c @@ -6118,6 +6118,15 @@ SCM_DEFINE (scm_integer_p, "integer?", 1, 0, 0, } #undef FUNC_NAME +SCM_DEFINE (scm_fixnum_p, "fixnum?", 1, 0, 0, + (SCM x), + "Return @code{#t} if @var{x} is a fixnum, @code{#f} otherwise.") +#define FUNC_NAME s_scm_fixnum_p +{ + return scm_from_bool (SCM_I_INUMP (x)); +} +#undef FUNC_NAME + SCM scm_i_num_eq_p (SCM, SCM, SCM); SCM_PRIMITIVE_GENERIC (scm_i_num_eq_p, "=", 0, 2, 1, diff --git a/libguile/numbers.h b/libguile/numbers.h index ab96981..fb97785 100644 --- a/libguile/numbers.h +++ b/libguile/numbers.h @@ -240,6 +240,7 @@ SCM_API SCM scm_complex_p (SCM x); SCM_API SCM scm_real_p (SCM x); SCM_API SCM scm_rational_p (SCM z); SCM_API SCM scm_integer_p (SCM x); +SCM_API SCM scm_fixnum_p (SCM x); SCM_API SCM scm_inexact_p (SCM x); SCM_API SCM scm_num_eq_p (SCM x, SCM y); SCM_API SCM scm_less_p (SCM x, SCM y); diff --git a/libguile/vm-i-scheme.c b/libguile/vm-i-scheme.c index 19b48c5..7d975aa 100644 --- a/libguile/vm-i-scheme.c +++ b/libguile/vm-i-scheme.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2009, 2010 Free Software Foundation, Inc. +/* Copyright (C) 2001, 2009, 2010, 2011 Free Software Foundation, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License @@ -111,12 +111,18 @@ VM_DEFINE_FUNCTION (139, vectorp, "vector?", 1) RETURN (scm_from_bool (SCM_I_IS_VECTOR (x))); } +VM_DEFINE_FUNCTION (140, fixnump, "fixnum?", 1) +{ + ARGS1 (x); + RETURN (scm_from_bool (SCM_I_INUMP (x))); +} + \f /* * Basic data */ -VM_DEFINE_FUNCTION (140, cons, "cons", 2) +VM_DEFINE_FUNCTION (141, cons, "cons", 2) { ARGS2 (x, y); CONS (x, x, y); @@ -130,21 +136,21 @@ VM_DEFINE_FUNCTION (140, cons, "cons", 2) goto vm_error_not_a_pair; \ } -VM_DEFINE_FUNCTION (141, car, "car", 1) +VM_DEFINE_FUNCTION (142, car, "car", 1) { ARGS1 (x); VM_VALIDATE_CONS (x, "car"); RETURN (SCM_CAR (x)); } -VM_DEFINE_FUNCTION (142, cdr, "cdr", 1) +VM_DEFINE_FUNCTION (143, cdr, "cdr", 1) { ARGS1 (x); VM_VALIDATE_CONS (x, "cdr"); RETURN (SCM_CDR (x)); } -VM_DEFINE_INSTRUCTION (143, set_car, "set-car!", 0, 2, 0) +VM_DEFINE_INSTRUCTION (144, set_car, "set-car!", 0, 2, 0) { SCM x, y; POP (y); @@ -154,7 +160,7 @@ VM_DEFINE_INSTRUCTION (143, set_car, "set-car!", 0, 2, 0) NEXT; } -VM_DEFINE_INSTRUCTION (144, set_cdr, "set-cdr!", 0, 2, 0) +VM_DEFINE_INSTRUCTION (145, set_cdr, "set-cdr!", 0, 2, 0) { SCM x, y; POP (y); @@ -180,27 +186,27 @@ VM_DEFINE_INSTRUCTION (144, set_cdr, "set-cdr!", 0, 2, 0) RETURN (srel (x, y)); \ } -VM_DEFINE_FUNCTION (145, ee, "ee?", 2) +VM_DEFINE_FUNCTION (146, ee, "ee?", 2) { REL (==, scm_num_eq_p); } -VM_DEFINE_FUNCTION (146, lt, "lt?", 2) +VM_DEFINE_FUNCTION (147, lt, "lt?", 2) { REL (<, scm_less_p); } -VM_DEFINE_FUNCTION (147, le, "le?", 2) +VM_DEFINE_FUNCTION (148, le, "le?", 2) { REL (<=, scm_leq_p); } -VM_DEFINE_FUNCTION (148, gt, "gt?", 2) +VM_DEFINE_FUNCTION (149, gt, "gt?", 2) { REL (>, scm_gr_p); } -VM_DEFINE_FUNCTION (149, ge, "ge?", 2) +VM_DEFINE_FUNCTION (150, ge, "ge?", 2) { REL (>=, scm_geq_p); } @@ -282,7 +288,7 @@ VM_DEFINE_FUNCTION (149, ge, "ge?", 2) #endif -VM_DEFINE_FUNCTION (150, add, "add", 2) +VM_DEFINE_FUNCTION (151, add, "add", 2) { #ifndef ASM_ADD FUNC2 (+, scm_sum); @@ -294,7 +300,7 @@ VM_DEFINE_FUNCTION (150, add, "add", 2) #endif } -VM_DEFINE_FUNCTION (151, add1, "add1", 1) +VM_DEFINE_FUNCTION (152, add1, "add1", 1) { ARGS1 (x); @@ -316,7 +322,7 @@ VM_DEFINE_FUNCTION (151, add1, "add1", 1) RETURN (scm_sum (x, SCM_I_MAKINUM (1))); } -VM_DEFINE_FUNCTION (152, sub, "sub", 2) +VM_DEFINE_FUNCTION (153, sub, "sub", 2) { #ifndef ASM_SUB FUNC2 (-, scm_difference); @@ -328,7 +334,7 @@ VM_DEFINE_FUNCTION (152, sub, "sub", 2) #endif } -VM_DEFINE_FUNCTION (153, sub1, "sub1", 1) +VM_DEFINE_FUNCTION (154, sub1, "sub1", 1) { ARGS1 (x); @@ -353,42 +359,42 @@ VM_DEFINE_FUNCTION (153, sub1, "sub1", 1) # undef ASM_ADD # undef ASM_SUB -VM_DEFINE_FUNCTION (154, mul, "mul", 2) +VM_DEFINE_FUNCTION (155, mul, "mul", 2) { ARGS2 (x, y); SYNC_REGISTER (); RETURN (scm_product (x, y)); } -VM_DEFINE_FUNCTION (155, div, "div", 2) +VM_DEFINE_FUNCTION (156, div, "div", 2) { ARGS2 (x, y); SYNC_REGISTER (); RETURN (scm_divide (x, y)); } -VM_DEFINE_FUNCTION (156, quo, "quo", 2) +VM_DEFINE_FUNCTION (157, quo, "quo", 2) { ARGS2 (x, y); SYNC_REGISTER (); RETURN (scm_quotient (x, y)); } -VM_DEFINE_FUNCTION (157, rem, "rem", 2) +VM_DEFINE_FUNCTION (158, rem, "rem", 2) { ARGS2 (x, y); SYNC_REGISTER (); RETURN (scm_remainder (x, y)); } -VM_DEFINE_FUNCTION (158, mod, "mod", 2) +VM_DEFINE_FUNCTION (159, mod, "mod", 2) { ARGS2 (x, y); SYNC_REGISTER (); RETURN (scm_modulo (x, y)); } -VM_DEFINE_FUNCTION (159, ash, "ash", 2) +VM_DEFINE_FUNCTION (160, ash, "ash", 2) { ARGS2 (x, y); if (SCM_I_INUMP (x) && SCM_I_INUMP (y)) @@ -417,7 +423,7 @@ VM_DEFINE_FUNCTION (159, ash, "ash", 2) RETURN (scm_ash (x, y)); } -VM_DEFINE_FUNCTION (160, logand, "logand", 2) +VM_DEFINE_FUNCTION (161, logand, "logand", 2) { ARGS2 (x, y); if (SCM_I_INUMP (x) && SCM_I_INUMP (y)) @@ -426,7 +432,7 @@ VM_DEFINE_FUNCTION (160, logand, "logand", 2) RETURN (scm_logand (x, y)); } -VM_DEFINE_FUNCTION (161, logior, "logior", 2) +VM_DEFINE_FUNCTION (162, logior, "logior", 2) { ARGS2 (x, y); if (SCM_I_INUMP (x) && SCM_I_INUMP (y)) @@ -435,7 +441,7 @@ VM_DEFINE_FUNCTION (161, logior, "logior", 2) RETURN (scm_logior (x, y)); } -VM_DEFINE_FUNCTION (162, logxor, "logxor", 2) +VM_DEFINE_FUNCTION (163, logxor, "logxor", 2) { ARGS2 (x, y); if (SCM_I_INUMP (x) && SCM_I_INUMP (y)) @@ -449,7 +455,7 @@ VM_DEFINE_FUNCTION (162, logxor, "logxor", 2) * Vectors and arrays */ -VM_DEFINE_FUNCTION (163, vector_ref, "vector-ref", 2) +VM_DEFINE_FUNCTION (164, vector_ref, "vector-ref", 2) { scm_t_signed_bits i = 0; ARGS2 (vect, idx); @@ -465,7 +471,7 @@ VM_DEFINE_FUNCTION (163, vector_ref, "vector-ref", 2) } } -VM_DEFINE_INSTRUCTION (164, vector_set, "vector-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (165, vector_set, "vector-set", 0, 3, 0) { scm_t_signed_bits i = 0; SCM vect, idx, val; @@ -483,7 +489,7 @@ VM_DEFINE_INSTRUCTION (164, vector_set, "vector-set", 0, 3, 0) NEXT; } -VM_DEFINE_INSTRUCTION (165, make_array, "make-array", 3, -1, 1) +VM_DEFINE_INSTRUCTION (166, make_array, "make-array", 3, -1, 1) { scm_t_uint32 len; SCM shape, ret; @@ -512,20 +518,20 @@ VM_DEFINE_INSTRUCTION (165, make_array, "make-array", 3, -1, 1) goto vm_error_not_a_struct; \ } -VM_DEFINE_FUNCTION (166, struct_p, "struct?", 1) +VM_DEFINE_FUNCTION (167, struct_p, "struct?", 1) { ARGS1 (obj); RETURN (scm_from_bool (SCM_STRUCTP (obj))); } -VM_DEFINE_FUNCTION (167, struct_vtable, "struct-vtable", 1) +VM_DEFINE_FUNCTION (168, struct_vtable, "struct-vtable", 1) { ARGS1 (obj); VM_VALIDATE_STRUCT (obj, "struct_vtable"); RETURN (SCM_STRUCT_VTABLE (obj)); } -VM_DEFINE_INSTRUCTION (168, make_struct, "make-struct", 2, -1, 1) +VM_DEFINE_INSTRUCTION (169, make_struct, "make-struct", 2, -1, 1) { unsigned h = FETCH (); unsigned l = FETCH (); @@ -558,7 +564,7 @@ VM_DEFINE_INSTRUCTION (168, make_struct, "make-struct", 2, -1, 1) NEXT; } -VM_DEFINE_FUNCTION (169, struct_ref, "struct-ref", 2) +VM_DEFINE_FUNCTION (170, struct_ref, "struct-ref", 2) { ARGS2 (obj, pos); @@ -588,7 +594,7 @@ VM_DEFINE_FUNCTION (169, struct_ref, "struct-ref", 2) RETURN (scm_struct_ref (obj, pos)); } -VM_DEFINE_FUNCTION (170, struct_set, "struct-set", 3) +VM_DEFINE_FUNCTION (171, struct_set, "struct-set", 3) { ARGS3 (obj, pos, val); @@ -622,7 +628,7 @@ VM_DEFINE_FUNCTION (170, struct_set, "struct-set", 3) /* * GOOPS support */ -VM_DEFINE_FUNCTION (171, class_of, "class-of", 1) +VM_DEFINE_FUNCTION (172, class_of, "class-of", 1) { ARGS1 (obj); if (SCM_INSTANCEP (obj)) @@ -632,7 +638,7 @@ VM_DEFINE_FUNCTION (171, class_of, "class-of", 1) } /* FIXME: No checking whatsoever. */ -VM_DEFINE_FUNCTION (172, slot_ref, "slot-ref", 2) +VM_DEFINE_FUNCTION (173, slot_ref, "slot-ref", 2) { size_t slot; ARGS2 (instance, idx); @@ -641,7 +647,7 @@ VM_DEFINE_FUNCTION (172, slot_ref, "slot-ref", 2) } /* FIXME: No checking whatsoever. */ -VM_DEFINE_INSTRUCTION (173, slot_set, "slot-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (174, slot_set, "slot-set", 0, 3, 0) { SCM instance, idx, val; size_t slot; @@ -686,21 +692,21 @@ VM_DEFINE_INSTRUCTION (173, slot_set, "slot-set", 0, 3, 0) #define ALIGNED_P(ptr, type) \ ((scm_t_uintptr) (ptr) % alignof (type) == 0) -VM_DEFINE_FUNCTION (174, bv_u16_ref, "bv-u16-ref", 3) +VM_DEFINE_FUNCTION (175, bv_u16_ref, "bv-u16-ref", 3) BV_REF_WITH_ENDIANNESS (u16, u16) -VM_DEFINE_FUNCTION (175, bv_s16_ref, "bv-s16-ref", 3) +VM_DEFINE_FUNCTION (176, bv_s16_ref, "bv-s16-ref", 3) BV_REF_WITH_ENDIANNESS (s16, s16) -VM_DEFINE_FUNCTION (176, bv_u32_ref, "bv-u32-ref", 3) +VM_DEFINE_FUNCTION (177, bv_u32_ref, "bv-u32-ref", 3) BV_REF_WITH_ENDIANNESS (u32, u32) -VM_DEFINE_FUNCTION (177, bv_s32_ref, "bv-s32-ref", 3) +VM_DEFINE_FUNCTION (178, bv_s32_ref, "bv-s32-ref", 3) BV_REF_WITH_ENDIANNESS (s32, s32) -VM_DEFINE_FUNCTION (178, bv_u64_ref, "bv-u64-ref", 3) +VM_DEFINE_FUNCTION (179, bv_u64_ref, "bv-u64-ref", 3) BV_REF_WITH_ENDIANNESS (u64, u64) -VM_DEFINE_FUNCTION (179, bv_s64_ref, "bv-s64-ref", 3) +VM_DEFINE_FUNCTION (180, bv_s64_ref, "bv-s64-ref", 3) BV_REF_WITH_ENDIANNESS (s64, s64) -VM_DEFINE_FUNCTION (180, bv_f32_ref, "bv-f32-ref", 3) +VM_DEFINE_FUNCTION (181, bv_f32_ref, "bv-f32-ref", 3) BV_REF_WITH_ENDIANNESS (f32, ieee_single) -VM_DEFINE_FUNCTION (181, bv_f64_ref, "bv-f64-ref", 3) +VM_DEFINE_FUNCTION (182, bv_f64_ref, "bv-f64-ref", 3) BV_REF_WITH_ENDIANNESS (f64, ieee_double) #undef BV_REF_WITH_ENDIANNESS @@ -778,33 +784,33 @@ BV_REF_WITH_ENDIANNESS (f64, ieee_double) RETURN (scm_bytevector_ ## fn_stem ## _native_ref (bv, idx)); \ } -VM_DEFINE_FUNCTION (182, bv_u8_ref, "bv-u8-ref", 2) +VM_DEFINE_FUNCTION (183, bv_u8_ref, "bv-u8-ref", 2) BV_FIXABLE_INT_REF (u8, u8, uint8, 1) -VM_DEFINE_FUNCTION (183, bv_s8_ref, "bv-s8-ref", 2) +VM_DEFINE_FUNCTION (184, bv_s8_ref, "bv-s8-ref", 2) BV_FIXABLE_INT_REF (s8, s8, int8, 1) -VM_DEFINE_FUNCTION (184, bv_u16_native_ref, "bv-u16-native-ref", 2) +VM_DEFINE_FUNCTION (185, bv_u16_native_ref, "bv-u16-native-ref", 2) BV_FIXABLE_INT_REF (u16, u16_native, uint16, 2) -VM_DEFINE_FUNCTION (185, bv_s16_native_ref, "bv-s16-native-ref", 2) +VM_DEFINE_FUNCTION (186, bv_s16_native_ref, "bv-s16-native-ref", 2) BV_FIXABLE_INT_REF (s16, s16_native, int16, 2) -VM_DEFINE_FUNCTION (186, bv_u32_native_ref, "bv-u32-native-ref", 2) +VM_DEFINE_FUNCTION (187, bv_u32_native_ref, "bv-u32-native-ref", 2) #if SIZEOF_VOID_P > 4 BV_FIXABLE_INT_REF (u32, u32_native, uint32, 4) #else BV_INT_REF (u32, uint32, 4) #endif -VM_DEFINE_FUNCTION (187, bv_s32_native_ref, "bv-s32-native-ref", 2) +VM_DEFINE_FUNCTION (188, bv_s32_native_ref, "bv-s32-native-ref", 2) #if SIZEOF_VOID_P > 4 BV_FIXABLE_INT_REF (s32, s32_native, int32, 4) #else BV_INT_REF (s32, int32, 4) #endif -VM_DEFINE_FUNCTION (188, bv_u64_native_ref, "bv-u64-native-ref", 2) +VM_DEFINE_FUNCTION (189, bv_u64_native_ref, "bv-u64-native-ref", 2) BV_INT_REF (u64, uint64, 8) -VM_DEFINE_FUNCTION (189, bv_s64_native_ref, "bv-s64-native-ref", 2) +VM_DEFINE_FUNCTION (190, bv_s64_native_ref, "bv-s64-native-ref", 2) BV_INT_REF (s64, int64, 8) -VM_DEFINE_FUNCTION (190, bv_f32_native_ref, "bv-f32-native-ref", 2) +VM_DEFINE_FUNCTION (191, bv_f32_native_ref, "bv-f32-native-ref", 2) BV_FLOAT_REF (f32, ieee_single, float, 4) -VM_DEFINE_FUNCTION (191, bv_f64_native_ref, "bv-f64-native-ref", 2) +VM_DEFINE_FUNCTION (192, bv_f64_native_ref, "bv-f64-native-ref", 2) BV_FLOAT_REF (f64, ieee_double, double, 8) #undef BV_FIXABLE_INT_REF @@ -826,21 +832,21 @@ BV_FLOAT_REF (f64, ieee_double, double, 8) } \ } -VM_DEFINE_INSTRUCTION (192, bv_u16_set, "bv-u16-set", 0, 4, 0) +VM_DEFINE_INSTRUCTION (193, bv_u16_set, "bv-u16-set", 0, 4, 0) BV_SET_WITH_ENDIANNESS (u16, u16) -VM_DEFINE_INSTRUCTION (193, bv_s16_set, "bv-s16-set", 0, 4, 0) +VM_DEFINE_INSTRUCTION (194, bv_s16_set, "bv-s16-set", 0, 4, 0) BV_SET_WITH_ENDIANNESS (s16, s16) -VM_DEFINE_INSTRUCTION (194, bv_u32_set, "bv-u32-set", 0, 4, 0) +VM_DEFINE_INSTRUCTION (195, bv_u32_set, "bv-u32-set", 0, 4, 0) BV_SET_WITH_ENDIANNESS (u32, u32) -VM_DEFINE_INSTRUCTION (195, bv_s32_set, "bv-s32-set", 0, 4, 0) +VM_DEFINE_INSTRUCTION (196, bv_s32_set, "bv-s32-set", 0, 4, 0) BV_SET_WITH_ENDIANNESS (s32, s32) -VM_DEFINE_INSTRUCTION (196, bv_u64_set, "bv-u64-set", 0, 4, 0) +VM_DEFINE_INSTRUCTION (197, bv_u64_set, "bv-u64-set", 0, 4, 0) BV_SET_WITH_ENDIANNESS (u64, u64) -VM_DEFINE_INSTRUCTION (197, bv_s64_set, "bv-s64-set", 0, 4, 0) +VM_DEFINE_INSTRUCTION (198, bv_s64_set, "bv-s64-set", 0, 4, 0) BV_SET_WITH_ENDIANNESS (s64, s64) -VM_DEFINE_INSTRUCTION (198, bv_f32_set, "bv-f32-set", 0, 4, 0) +VM_DEFINE_INSTRUCTION (199, bv_f32_set, "bv-f32-set", 0, 4, 0) BV_SET_WITH_ENDIANNESS (f32, ieee_single) -VM_DEFINE_INSTRUCTION (199, bv_f64_set, "bv-f64-set", 0, 4, 0) +VM_DEFINE_INSTRUCTION (200, bv_f64_set, "bv-f64-set", 0, 4, 0) BV_SET_WITH_ENDIANNESS (f64, ieee_double) #undef BV_SET_WITH_ENDIANNESS @@ -911,33 +917,33 @@ BV_SET_WITH_ENDIANNESS (f64, ieee_double) NEXT; \ } -VM_DEFINE_INSTRUCTION (200, bv_u8_set, "bv-u8-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (201, bv_u8_set, "bv-u8-set", 0, 3, 0) BV_FIXABLE_INT_SET (u8, u8, uint8, 0, SCM_T_UINT8_MAX, 1) -VM_DEFINE_INSTRUCTION (201, bv_s8_set, "bv-s8-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (202, bv_s8_set, "bv-s8-set", 0, 3, 0) BV_FIXABLE_INT_SET (s8, s8, int8, SCM_T_INT8_MIN, SCM_T_INT8_MAX, 1) -VM_DEFINE_INSTRUCTION (202, bv_u16_native_set, "bv-u16-native-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (203, bv_u16_native_set, "bv-u16-native-set", 0, 3, 0) BV_FIXABLE_INT_SET (u16, u16_native, uint16, 0, SCM_T_UINT16_MAX, 2) -VM_DEFINE_INSTRUCTION (203, bv_s16_native_set, "bv-s16-native-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (204, bv_s16_native_set, "bv-s16-native-set", 0, 3, 0) BV_FIXABLE_INT_SET (s16, s16_native, int16, SCM_T_INT16_MIN, SCM_T_INT16_MAX, 2) -VM_DEFINE_INSTRUCTION (204, bv_u32_native_set, "bv-u32-native-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (205, bv_u32_native_set, "bv-u32-native-set", 0, 3, 0) #if SIZEOF_VOID_P > 4 BV_FIXABLE_INT_SET (u32, u32_native, uint32, 0, SCM_T_UINT32_MAX, 4) #else BV_INT_SET (u32, uint32, 4) #endif -VM_DEFINE_INSTRUCTION (205, bv_s32_native_set, "bv-s32-native-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (206, bv_s32_native_set, "bv-s32-native-set", 0, 3, 0) #if SIZEOF_VOID_P > 4 BV_FIXABLE_INT_SET (s32, s32_native, int32, SCM_T_INT32_MIN, SCM_T_INT32_MAX, 4) #else BV_INT_SET (s32, int32, 4) #endif -VM_DEFINE_INSTRUCTION (206, bv_u64_native_set, "bv-u64-native-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (207, bv_u64_native_set, "bv-u64-native-set", 0, 3, 0) BV_INT_SET (u64, uint64, 8) -VM_DEFINE_INSTRUCTION (207, bv_s64_native_set, "bv-s64-native-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (208, bv_s64_native_set, "bv-s64-native-set", 0, 3, 0) BV_INT_SET (s64, int64, 8) -VM_DEFINE_INSTRUCTION (208, bv_f32_native_set, "bv-f32-native-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (209, bv_f32_native_set, "bv-f32-native-set", 0, 3, 0) BV_FLOAT_SET (f32, ieee_single, float, 4) -VM_DEFINE_INSTRUCTION (209, bv_f64_native_set, "bv-f64-native-set", 0, 3, 0) +VM_DEFINE_INSTRUCTION (210, bv_f64_native_set, "bv-f64-native-set", 0, 3, 0) BV_FLOAT_SET (f64, ieee_double, double, 8) #undef BV_FIXABLE_INT_SET diff --git a/module/language/tree-il/compile-glil.scm b/module/language/tree-il/compile-glil.scm index 23648cd..4058f90 100644 --- a/module/language/tree-il/compile-glil.scm +++ b/module/language/tree-il/compile-glil.scm @@ -1,6 +1,6 @@ ;;; TREE-IL -> GLIL compiler -;; Copyright (C) 2001,2008,2009,2010 Free Software Foundation, Inc. +;; Copyright (C) 2001,2008,2009,2010,2011 Free Software Foundation, Inc. ;;;; This library is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU Lesser General Public @@ -108,6 +108,7 @@ ((list? . 1) . list?) ((symbol? . 1) . symbol?) ((vector? . 1) . vector?) + ((fixnum? . 1) . fixnum?) (list . list) (vector . vector) ((class-of . 1) . class-of) diff --git a/module/language/tree-il/primitives.scm b/module/language/tree-il/primitives.scm index 316a462..6e63691 100644 --- a/module/language/tree-il/primitives.scm +++ b/module/language/tree-il/primitives.scm @@ -1,6 +1,6 @@ ;;; open-coding primitive procedures -;; Copyright (C) 2009, 2010 Free Software Foundation, Inc. +;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc. ;;;; This library is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU Lesser General Public @@ -43,7 +43,7 @@ + * - / 1- 1+ quotient remainder modulo ash logand logior logxor not - pair? null? list? symbol? vector? acons cons cons* + fixnum? pair? null? list? symbol? vector? acons cons cons* list vector -- 1.7.4.1 ^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH] Add `fixnum?' VM primitive 2011-03-23 2:19 ` [PATCH] Add `fixnum?' VM primitive Andreas Rottmann @ 2011-03-24 22:01 ` Ludovic Courtès 2011-03-24 23:51 ` Andreas Rottmann 2011-03-29 10:48 ` Andy Wingo 0 siblings, 2 replies; 12+ messages in thread From: Ludovic Courtès @ 2011-03-24 22:01 UTC (permalink / raw) To: guile-devel Hi! Andreas Rottmann <a.rottmann@gmx.at> writes: > +SCM_DEFINE (scm_fixnum_p, "fixnum?", 1, 0, 0, > + (SCM x), > + "Return @code{#t} if @var{x} is a fixnum, @code{#f} otherwise.") > +#define FUNC_NAME s_scm_fixnum_p > +{ > + return scm_from_bool (SCM_I_INUMP (x)); > +} > +#undef FUNC_NAME For 2.0 I think you could go with this wonderful hack: (define (fixnum? x) (not (= 0 (logand 2 (object-address x))))) (An inlinable variant thereof, as done in srfi-9.scm.) For ‘master’ your patch looks good to me modulo a few details. Why did you need to renumber VM opcodes? Also, I’d prefer not to have ‘fixnum?’ in the default name space because: 1. In Guile parlance, it’d rather be ‘immediate-number?’ (info "(guile) Immediate objects"). 2. I think this fixnum thing is a breach in the numerical tower. Thanks, Ludo’. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Add `fixnum?' VM primitive 2011-03-24 22:01 ` Ludovic Courtès @ 2011-03-24 23:51 ` Andreas Rottmann 2011-03-25 10:40 ` Ludovic Courtès 2011-03-29 10:52 ` Andy Wingo 2011-03-29 10:48 ` Andy Wingo 1 sibling, 2 replies; 12+ messages in thread From: Andreas Rottmann @ 2011-03-24 23:51 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel ludo@gnu.org (Ludovic Courtès) writes: > Hi! > > Andreas Rottmann <a.rottmann@gmx.at> writes: > >> +SCM_DEFINE (scm_fixnum_p, "fixnum?", 1, 0, 0, >> + (SCM x), >> + "Return @code{#t} if @var{x} is a fixnum, @code{#f} otherwise.") >> +#define FUNC_NAME s_scm_fixnum_p >> +{ >> + return scm_from_bool (SCM_I_INUMP (x)); >> +} >> +#undef FUNC_NAME > > For 2.0 I think you could go with this wonderful hack: > > (define (fixnum? x) > (not (= 0 (logand 2 (object-address x))))) > > (An inlinable variant thereof, as done in srfi-9.scm.) > Excellent. Should we put `define-inlinable' in some common place to avoid code duplication? If so, where? > For ‘master’ your patch looks good to me modulo a few details. Why did > you need to renumber VM opcodes? > I didn't need to do so, but the neat order elicited my instinct to keep it ;-). I'll revert that part; should I then put `fixnum?' at the end? > Also, I’d prefer not to have ‘fixnum?’ in the default name space > because: > > 1. In Guile parlance, it’d rather be ‘immediate-number?’ (info > "(guile) Immediate objects"). > > 2. I think this fixnum thing is a breach in the numerical tower. > I'm fine with moving it out of the default namespace (I was actually worried about that, too). However, I vaguely remember the compiler not outputting the opcode when I did not define `fixnum?' as a C-based procedure in the default namespace, but I might be mistaken. I would appreciate pointer(s) of where to look to learn about how the compiler decides if some procedure can be emitted as an opcode (but I think I would be able to figure that out myself, but pointers would be nice nevertheless ;-)). Regards, Rotty -- Andreas Rottmann -- <http://rotty.yi.org/> ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Add `fixnum?' VM primitive 2011-03-24 23:51 ` Andreas Rottmann @ 2011-03-25 10:40 ` Ludovic Courtès 2011-03-25 11:50 ` Andreas Rottmann 2011-03-28 18:48 ` Ken Raeburn 2011-03-29 10:52 ` Andy Wingo 1 sibling, 2 replies; 12+ messages in thread From: Ludovic Courtès @ 2011-03-25 10:40 UTC (permalink / raw) To: Andreas Rottmann; +Cc: guile-devel Hello! Andreas Rottmann <a.rottmann@gmx.at> writes: > ludo@gnu.org (Ludovic Courtès) writes: > >> Hi! >> >> Andreas Rottmann <a.rottmann@gmx.at> writes: >> >>> +SCM_DEFINE (scm_fixnum_p, "fixnum?", 1, 0, 0, >>> + (SCM x), >>> + "Return @code{#t} if @var{x} is a fixnum, @code{#f} otherwise.") >>> +#define FUNC_NAME s_scm_fixnum_p >>> +{ >>> + return scm_from_bool (SCM_I_INUMP (x)); >>> +} >>> +#undef FUNC_NAME >> >> For 2.0 I think you could go with this wonderful hack: >> >> (define (fixnum? x) >> (not (= 0 (logand 2 (object-address x))))) >> >> (An inlinable variant thereof, as done in srfi-9.scm.) >> > Excellent. Should we put `define-inlinable' in some common place to > avoid code duplication? If so, where? That would be nice, if we are confident that it’s not broken any more. ;-) ISTR that Chez’ ‘define-integrable’ macro is hairier than that, so I wonder if our implementation is too naive. Thoughts? >> For ‘master’ your patch looks good to me modulo a few details. Why did >> you need to renumber VM opcodes? >> > I didn't need to do so, but the neat order elicited my instinct to keep > it ;-). I'll revert that part; should I then put `fixnum?' at the end? Yes please. I guess we could even add the new opcode in stable-2.0 because Andy tweaked objcode version check to be smarter—i.e., 2.0.1 would be able to read objcodes produced by either 2.0.1 or 2.0.0. WDYT? >> Also, I’d prefer not to have ‘fixnum?’ in the default name space >> because: >> >> 1. In Guile parlance, it’d rather be ‘immediate-number?’ (info >> "(guile) Immediate objects"). >> >> 2. I think this fixnum thing is a breach in the numerical tower. >> > I'm fine with moving it out of the default namespace (I was actually > worried about that, too). However, I vaguely remember the compiler not > outputting the opcode when I did not define `fixnum?' as a C-based > procedure in the default namespace, but I might be mistaken. I would > appreciate pointer(s) of where to look to learn about how the compiler > decides if some procedure can be emitted as an opcode (but I think I > would be able to figure that out myself, but pointers would be nice > nevertheless ;-)). In (language tree-il primitives), there’s ‘add-interesting-primitive!’, which checks whether the primitive name is bound in the current module. BTW why does the ZIP implementation in Industria keep calling ‘fixnum?’? Thanks, Ludo’. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Add `fixnum?' VM primitive 2011-03-25 10:40 ` Ludovic Courtès @ 2011-03-25 11:50 ` Andreas Rottmann 2011-03-25 13:21 ` Ludovic Courtès 2011-03-28 18:48 ` Ken Raeburn 1 sibling, 1 reply; 12+ messages in thread From: Andreas Rottmann @ 2011-03-25 11:50 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel ludo@gnu.org (Ludovic Courtès) writes: > Hello! > > Andreas Rottmann <a.rottmann@gmx.at> writes: > >> ludo@gnu.org (Ludovic Courtès) writes: >> >>> Hi! >>> >>> Andreas Rottmann <a.rottmann@gmx.at> writes: >>> >>>> +SCM_DEFINE (scm_fixnum_p, "fixnum?", 1, 0, 0, >>>> + (SCM x), >>>> + "Return @code{#t} if @var{x} is a fixnum, @code{#f} otherwise.") >>>> +#define FUNC_NAME s_scm_fixnum_p >>>> +{ >>>> + return scm_from_bool (SCM_I_INUMP (x)); >>>> +} >>>> +#undef FUNC_NAME >>> >>> For 2.0 I think you could go with this wonderful hack: >>> >>> (define (fixnum? x) >>> (not (= 0 (logand 2 (object-address x))))) >>> >>> (An inlinable variant thereof, as done in srfi-9.scm.) >>> >> Excellent. Should we put `define-inlinable' in some common place to >> avoid code duplication? If so, where? > > That would be nice, if we are confident that it’s not broken any more. ;-) > ISTR that Chez’ ‘define-integrable’ macro is hairier than that, so I > wonder if our implementation is too naive. Thoughts? > I'll have another look at that; if it turns out it's good, would `(ice-9 inline)' be a proper place for it? >>> For ‘master’ your patch looks good to me modulo a few details. Why did >>> you need to renumber VM opcodes? >>> >> I didn't need to do so, but the neat order elicited my instinct to keep >> it ;-). I'll revert that part; should I then put `fixnum?' at the end? > > Yes please. > > I guess we could even add the new opcode in stable-2.0 because Andy > tweaked objcode version check to be smarter—i.e., 2.0.1 would be able to > read objcodes produced by either 2.0.1 or 2.0.0. > > WDYT? > That would be nice indeed. >>> Also, I’d prefer not to have ‘fixnum?’ in the default name space >>> because: >>> >>> 1. In Guile parlance, it’d rather be ‘immediate-number?’ (info >>> "(guile) Immediate objects"). >>> >>> 2. I think this fixnum thing is a breach in the numerical tower. >>> >> I'm fine with moving it out of the default namespace (I was actually >> worried about that, too). However, I vaguely remember the compiler not >> outputting the opcode when I did not define `fixnum?' as a C-based >> procedure in the default namespace, but I might be mistaken. I would >> appreciate pointer(s) of where to look to learn about how the compiler >> decides if some procedure can be emitted as an opcode (but I think I >> would be able to figure that out myself, but pointers would be nice >> nevertheless ;-)). > > In (language tree-il primitives), there’s ‘add-interesting-primitive!’, > which checks whether the primitive name is bound in the current module. > OK, I'll experiment with that. > BTW why does the ZIP implementation in Industria keep calling ‘fixnum?’? > It's actually the implementation of `(rnrs arithmetic fixnums)' that calls fixnum for almost every operation; also see my other mail [0], where I talk about lifting that at least for some operations. [0] http://lists.gnu.org/archive/html/guile-devel/2011-03/msg00235.html Regards, Rotty -- Andreas Rottmann -- <http://rotty.yi.org/> ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Add `fixnum?' VM primitive 2011-03-25 11:50 ` Andreas Rottmann @ 2011-03-25 13:21 ` Ludovic Courtès 2011-03-29 11:03 ` Andy Wingo 0 siblings, 1 reply; 12+ messages in thread From: Ludovic Courtès @ 2011-03-25 13:21 UTC (permalink / raw) To: Andreas Rottmann; +Cc: guile-devel Hi! Andreas Rottmann <a.rottmann@gmx.at> writes: [...] >>> Excellent. Should we put `define-inlinable' in some common place to >>> avoid code duplication? If so, where? >> >> That would be nice, if we are confident that it’s not broken any more. ;-) >> ISTR that Chez’ ‘define-integrable’ macro is hairier than that, so I >> wonder if our implementation is too naive. Thoughts? >> > I'll have another look at that; if it turns out it's good, would `(ice-9 > inline)' be a proper place for it? Or even in the default name space, IMO. It should be documented with a warning, as done in the Elisp manual (info "(elisp) Inline Functions"). [...] >> BTW why does the ZIP implementation in Industria keep calling ‘fixnum?’? >> > It's actually the implementation of `(rnrs arithmetic fixnums)' that > calls fixnum for almost every operation; also see my other mail [0], > where I talk about lifting that at least for some operations. OK, I see. I think that a fixnum API sucks. In an ideal world, the implementation would be optimized such that you can be switch from fixnum to bignum with very little overhead: 1. The run-time support should be optimized to make arithmetic operations fast in the fixnum case. 2. When the compiler can statically determine that code is dealing with fixnums, it can generate code that uses untagged machine integers (that’s what Bigloo does, for instance). I’ve always thought that R5RS implicitly acknowledges the fact that implementations can provide almost costless fixnum/bignum transition. Thanks, Ludo’. ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Add `fixnum?' VM primitive 2011-03-25 13:21 ` Ludovic Courtès @ 2011-03-29 11:03 ` Andy Wingo 0 siblings, 0 replies; 12+ messages in thread From: Andy Wingo @ 2011-03-29 11:03 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel On Fri 25 Mar 2011 14:21, ludo@gnu.org (Ludovic Courtès) writes: > I think that a fixnum API sucks. In an ideal world, the implementation > would be optimized such that you can be switch from fixnum to bignum > with very little overhead: > > 1. The run-time support should be optimized to make arithmetic > operations fast in the fixnum case. > > 2. When the compiler can statically determine that code is dealing > with fixnums, it can generate code that uses untagged machine > integers (that’s what Bigloo does, for instance). > > I’ve always thought that R5RS implicitly acknowledges the fact that > implementations can provide almost costless fixnum/bignum transition. I rolled this one around for a while in my mind too, and came to the conclusion that the R6RS fxops can be helpful -- but only to a system that does native compilation. Such a system, for +, for example, would do: output = input-a + input-b if overflow-bit-set? promote to bignums; try again R6RS explicitly redefines this: output = input-a + input-b if overflow-bit-set? error So there's no more overhead than for normal +. However the fact that the result will always be a fixnum allows you to do more flow analysis. So even without unsafe compilation options, you can omit more fixnum? checks when you use fxops, and the amount of code emitted is smaller. But for Guile, right now, R6RS fxops have no advantage over the normal numeric operations. Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Add `fixnum?' VM primitive 2011-03-25 10:40 ` Ludovic Courtès 2011-03-25 11:50 ` Andreas Rottmann @ 2011-03-28 18:48 ` Ken Raeburn 2011-03-29 10:55 ` Andy Wingo 1 sibling, 1 reply; 12+ messages in thread From: Ken Raeburn @ 2011-03-28 18:48 UTC (permalink / raw) To: guile-devel On Mar 25, 2011, at 06:40, Ludovic Courtès wrote: > I guess we could even add the new opcode in stable-2.0 because Andy > tweaked objcode version check to be smarter—i.e., 2.0.1 would be able to > read objcodes produced by either 2.0.1 or 2.0.0. If I switch from a machine with 2.0.1 installed to a machine with 2.0.0 installed, with both mounting my home directory from the same place, will the 2.0.1 .go files break under the 2.0.0 installation? Ken ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Add `fixnum?' VM primitive 2011-03-28 18:48 ` Ken Raeburn @ 2011-03-29 10:55 ` Andy Wingo 0 siblings, 0 replies; 12+ messages in thread From: Andy Wingo @ 2011-03-29 10:55 UTC (permalink / raw) To: Ken Raeburn; +Cc: guile-devel On Mon 28 Mar 2011 20:48, Ken Raeburn <raeburn@raeburn.org> writes: > On Mar 25, 2011, at 06:40, Ludovic Courtès wrote: >> I guess we could even add the new opcode in stable-2.0 because Andy >> tweaked objcode version check to be smarter—i.e., 2.0.1 would be able > to >> read objcodes produced by either 2.0.1 or 2.0.0. > > If I switch from a machine with 2.0.1 installed to a machine with 2.0.0 > installed, with both mounting my home directory from the same place, > will the 2.0.1 .go files break under the 2.0.0 installation? If we bump the objcode minor version for 2.0.1, the 2.0.0 installation would detect 2.0.1 .go files, and raise an error. 2.0.1 could load a 2.0.0 .go file however. Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Add `fixnum?' VM primitive 2011-03-24 23:51 ` Andreas Rottmann 2011-03-25 10:40 ` Ludovic Courtès @ 2011-03-29 10:52 ` Andy Wingo 1 sibling, 0 replies; 12+ messages in thread From: Andy Wingo @ 2011-03-29 10:52 UTC (permalink / raw) To: Andreas Rottmann; +Cc: Ludovic Courtès, guile-devel On Fri 25 Mar 2011 00:51, Andreas Rottmann <a.rottmann@gmx.at> writes: >> For ‘master’ your patch looks good to me modulo a few details. Why did >> you need to renumber VM opcodes? >> > I didn't need to do so, but the neat order elicited my instinct to keep > it ;-). I'll revert that part; should I then put `fixnum?' at the > end? You can leave it where it is, if you wanted, and just give it a later number. Then when we renumber in 2.2, it will get a number in the "neat" order. All of those definitions expand out to `case' blocks, but I don't know what the compiler does with them. Does it actually put them in the order they appear? Does it put them in numerical order? I do know that it puts the UNLIKELY blocks at the end of the VM engine, which is important, but I don't know what it does with instructions for the various opcodes. Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH] Add `fixnum?' VM primitive 2011-03-24 22:01 ` Ludovic Courtès 2011-03-24 23:51 ` Andreas Rottmann @ 2011-03-29 10:48 ` Andy Wingo 1 sibling, 0 replies; 12+ messages in thread From: Andy Wingo @ 2011-03-29 10:48 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel On Thu 24 Mar 2011 23:01, ludo@gnu.org (Ludovic Courtès) writes: > Also, I’d prefer not to have ‘fixnum?’ in the default name space > because: > > 1. In Guile parlance, it’d rather be ‘immediate-number?’ (info > "(guile) Immediate objects"). Not related to Andreas' patch, but... I think our terminology is silly here. "Fixnum" is better IMO. Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2011-03-29 11:03 UTC | newest] Thread overview: 12+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2011-03-23 2:19 fixnum? VM primitive, increasing fixnum operation speed Andreas Rottmann 2011-03-23 2:19 ` [PATCH] Add `fixnum?' VM primitive Andreas Rottmann 2011-03-24 22:01 ` Ludovic Courtès 2011-03-24 23:51 ` Andreas Rottmann 2011-03-25 10:40 ` Ludovic Courtès 2011-03-25 11:50 ` Andreas Rottmann 2011-03-25 13:21 ` Ludovic Courtès 2011-03-29 11:03 ` Andy Wingo 2011-03-28 18:48 ` Ken Raeburn 2011-03-29 10:55 ` Andy Wingo 2011-03-29 10:52 ` Andy Wingo 2011-03-29 10:48 ` Andy Wingo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).