Tuesday, 2023-05-02

octaviusMy elf says unknown architecture, is that to do with the Makefile not supplying the relevant metadata, or with the objdump version (from Debian buster repos)00:00
programmerjakeyou need to use the powerpc64le objdump, the name is something like powerpc64le-linux-gnu-objdump00:01
octaviusThank you so much!00:02
octaviusI've been an idiot :)00:02
programmerjakeif you use the x86 objdump, it doesn't have the powerpc disassembly code compiled in00:02
octaviusYes, I've only been able to see the symbol tables so far00:03
octaviusI'll include this in the wiki page once I get the code running00:03
lkclghostmansd[m], awesome on the aliases00:09
lkclyes, sorry, i assumed you knew octavius that binutils versions are specifically-compiled for specific architectures00:15
lkcl(with ghostmansd[m] working on binutils compiled for ppc64 for us)00:15
lkcland it being in the Makefile(s)00:15
lkcli do appreciate there's a heck of a lot to keep track of00:16
programmerjakelkcl, can i try to improve the integer dct add/sub/mul/shift instruction's pseudocode?00:18
lkclprogrammerjake, no let markos_ handle it.00:21
octaviuslkcl, looking at the objdump -d, I don't actually see problems regarding the addresses. At address 0x0 cpu *should* branch to 0x12c (boot_entry). Boot_entry then eventually branches to main (0x1014). As I have already shown though, the verilator sim bram.dump goes through the ff00_0000/4/8, then gets stuck at 0x800.00:26
octaviusThe interesting thing is that this exact hello world code (C code, linker script, startup assembler) works on ls2 fpga. So why is the verilator so finicky?00:27
octaviusThis issue is why I've (foolishly) been avoiding doing any simulations at all, and just wanted to work on FPGAs00:27
lkclyou cannot inspect the inside of the FPGA.00:29
octaviusOf course, that's why sims are so useful00:29
lkclok so you could have diagnosed this yourself by looking at the RESET_ADDRESS in the Makefile00:30
octaviusI already have looked at the RESET_ADDRESS in the makefile00:30
lkcl 220 RESET_ADDRESS=65280 # 0xff00_0000>>1600:31
lkcldid you perform a full clean rebuild?00:31
octaviusYes, I always ran make clean before generating a new hello_world00:32
lkclthe RESET_ADDRESS #define says where the start address is, yes?00:32
lkclso if you are still executing simulations that start at address 0xff00_0000 when you have specifically and explicitly changed that line in the Makefile to 0x0000_0000 and it still starts at 0xff00_000000:32
octaviusYes, ff00 (which the VHDL then shifts 16 times to get ff00_0000)00:33
lkclthen you've not got rid of everything00:33
lkclso why are you expecting the simulation of the CPU to start at an address other than 0xff00_0000 ?00:33
octaviusI never changed the Makefile, only the powerpc.lds for the hello_world00:33
lkclso i repeat the question: why are you expecting the simulation of the CPU to start at an addres other than the one that is specified at line 220?00:34
octaviusI thought that the CPU expects the BRAM to start at 0xff00_0000, while the actual address on the BRAM side is 0x000:34
lkclstart address === RESET_ADDRESS00:34
lkclif you don't tell the CPU to start at the address that matches the linker script's expected start address, how is anything ever going to work?00:35
lkclthe verilator simulator is doing precisely and exactly what you've asked it to do.00:35
octaviusSo then the VHDL RESET_ADDRESS needs to change to 0x0?00:35
lkcl1. load a program into memory (probably at 0x0000_00000)00:35
lkcl2. start executing at 0xff00_0000.00:35
lkclwhat do you think?00:36
lkclor, more to the point, why did it not occur to you to experiment by changing it to anything-at-all and seeing what the effect is?00:36
octaviusYou told me NOT to change the VHDL, so I thought there was a way to do it00:36
lkclthat was before i realised you were using the microwatt_verilator directly00:37
octaviusYes, I was trying to try microwatt standalone, I apologise for not clarifying earlier00:37
lkclplus (apologies) i've been focussing on the RFCs00:37
lkcland, the HDL != "macro #define options"00:38
lkclyou definitely don't want to start modifying the vhdl itself (which strictly speaking isn't the same thing as the compile-time options)00:38
octaviusAh, that's what you meant00:38
lkclwell, kinda :)  honestly, i wasn't paying enough attention00:38
lkclhead-spinning from 17 RFCs00:39
octaviusOk, I'll make sure to be *even more* specific :)00:39
octaviusThen tomorrow I'll give some of them a re-read.00:39
lkclbut yes, i was expecting you to recompile the binary at address 0xff00_000000:39
octaviusAny RFCs that are going to be submitted soon?00:39
lkclthen run options in verilator which load the binary into simulated-memory at that address00:39
octavius"i was expecting you to recompile the binary at address 0xff00_0000" - This is what I was trying to do, but I have absolutely no idea which knob I meant to change in the .lds file for that00:40
lkclthere are several other examples around, some of which are macro'd (i mentioned that a couple of times already)00:41
octaviusThat's why I mentioned changing _start, which after looking at the disassembly, makes no difference00:41
lkclthere's some powerpc.lds.in files around somewhere00:41
lkclwhich *specifically* use macro-substitution of some #defines to create a powerpc.lds file00:41
lkcland guess what one of the options is?00:41
lkcltheee.... start addreeeeesss00:42
lkcli just can't remember which project does that.00:42
octaviusOh that would've been really useful about a week ago...but then I probably wouldn't been forced to actually learn some things XD00:42
octaviusYES! Changing the RESET_ADDRESS define makes microwatt-verilator work! YES!!!!!!!00:44
octaviusNow, need to find this generator file you mentioned00:44
lkclthey're around somewhere, i just can't remember where00:45
lkclfor loading the ls2 bootloader i think you'll find it does that trick00:46
lkcl(the one that reads from QSPI)00:46
lkclor, at least, the programs *it* loads.00:46
octaviusSure, I just wanted the Microwatt flow to be confirmed working00:46
octaviusWould make it easier for new contributors00:47
lkclno fantastic idea.00:52
octaviusFound it! https://git.libre-soc.org/?p=ls2.git;a=blob;f=hello_world/Makefile;h=50f039112f54165f8f6f7421ac62be1661889576;hb=HEAD#l900:54
octaviusI guess this is what you meant lkcl00:54
octaviusAlso I'd like to make a video going through the setup and running on Microwatt and Libre-SOC00:55
lkcl  28 powerpc.lds: powerpc.lds.S00:57
lkcl  29         $(CC) $(CFLAGS) -P -E powerpc.lds.S -o powerpc.lds00:57
lkclyep that's it.00:57
lkcl"gcc -E" - gcc's "macro" mode00:57
lkclthat's what i was expecting you to be using00:57
lkclBOOT_INIT_BASE as a #define *from CFLAGS* gets pre-process-substituted into powerpc.lds.S00:58
lkcl  21             -DBOOT_INIT_BASE=$(BOOT_INIT_BASE)00:58
lkcltoshywoshy, thx it's back01:04
octaviusIf you give me write access to Microwatt repo, I'll add this to the hello_world example later today01:06
octaviusOf course, testing it myself first :)01:06
octaviusBetter go to bed now, quite late. Thanks for the help lkcl, programmerjake!01:09
*** octavius <octavius!~octavius@> has quit IRC01:12
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has quit IRC07:26
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has joined #libre-soc07:27
ghostmansd[m]> lkcl: ghostmansd[m], awesome on the aliases07:33
ghostmansd[m]I liked most that the test is even able to demonstrate these are macros :-)07:33
ghostmansd[m]Anyway, if we have more of these ahead, we need to generate the records for them, too07:34
ghostmansd[m]If you're interested in this, I can think about configuration07:34
ghostmansd[m]I'll need some list of insns that have aliases, though, to at least use as example07:35
ghostmansd[m]I know about minmax, fminmax, and also vaguely recall something about grevlut et al.07:40
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has quit IRC07:55
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has joined #libre-soc07:56
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has quit IRC08:00
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has joined #libre-soc08:21
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has quit IRC08:30
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has joined #libre-soc08:30
programmerjakesounds like we need an aliases.csv08:50
programmerjakeor some other nicer format08:50
*** octavius <octavius!~octavius@> has joined #libre-soc09:46
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has quit IRC09:46
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has joined #libre-soc09:47
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has quit IRC10:24
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has joined #libre-soc10:31
lkclghostmansd[m], please wait until the next version of Power ISA is released. i cannot say more on that.10:51
lkclghostmansd[m], i have a specific self-contained task that's reasonably high priority if you're interested10:53
lkclwe need an offline instruction-ordering-analyser that models a (simple, initially v3.0-only) in-order core and gives estimates of instructions/clock10:54
lkclit needs to be *very* clear what is going on, nothing fancy (so no metaclasses)10:55
lkcland the Hazard Protection should be a straight simple bit-vector10:56
lkcl* take the Write result register number: set a bit10:56
lkcl* for all Read registers, check the corresponding bit.  if set, STALL (fake/model-stall that is)10:56
lkclthe input shall be:10:57
lkcl* instruction operands (as an assembler listing) plus an optional memory-address and whether it is read/written10:58
ghostmansd[m]lkcl, do you have a link to this task so that I could read and get a better idea?11:07
ghostmansd[m]I should have said "s strict no-no" once you mentioned "no metaclasses" :-D11:07
ghostmansd[m]Also, "high-priority" — what are the time constraints?11:27
lkclthere are no details yet - what i wrote above *is* the details13:30
ghostmansd[m]Does IPC stands for instructions per cycle?13:47
ghostmansd[m]Ok :-)13:47
ghostmansd[m]I'm a system programmer, so I had to ask13:47
ghostmansd[m]For me IPC means something else13:48
lkclso the basic principle is: some classes are needed which effectively "model" pipeline stages. fetch, decode, issue, execute13:48
lkclindeed :)13:48
lkcland those classes are (obviously) chained together13:48
ghostmansd[m]So, after all, this is a processor pipeline model?13:48
lkclthe first Model needed is of an in-order single-issue scalar core.13:49
ghostmansd[m]I developed part of this once, but it was too high-level13:49
ghostmansd[m]You probably heard of Intel Cofluent13:49
lkclah great, so you know what to expect. awesome13:49
lkclhave now13:49
lkclthis needs to be hardware-cycle-accurate13:50
ghostmansd[m]Well I actually modeled the only part of Nehalem, insn decoder13:50
ghostmansd[m]Not sure if this covers the task sufficiently13:50
lkclwhere the most important technical internal "flag" - the one that has the most influence in an in-order system - is the global "STALL" flag.13:50
ghostmansd[m]But at least something to start with13:50
ghostmansd[m]This STALL. Is it like a global barrier where all buses stop?13:51
ghostmansd[m]Ok, still vaguely recall something :-)13:51
lkclit tells the fetch to stop fetching, and because fetch has stopped decode has nothing to process13:51
lkclif decode has nothing to process, it has nothing to tell issue to do anything13:52
lkclif issue has nothing to do then execute (pipelines) run with an empty slot13:52
lkclso each "stall" has a ripple-effect down the chain-of-classes13:52
ghostmansd[m]Ok, where to start here?13:53
lkclliterally from scratch as a stand-alone program13:53
lkcltaking as input a file containing instructions:13:53
lkcladdi 3,4,513:54
lkclcmpi 1,2,3,413:54
lkclbut with some "augmentation" if it is a LD/ST, assume that there is the memory address as a comment13:54
lkclld 1,2(3) # 0x1234567813:54
lkclit'll need some design document (a real simple one), some discussion etc. to get the concept agreed13:55
ghostmansd[m]Any IRL examples to look at?13:56
lkclbut ultimately if this is more than 1,000 to 1,500 lines of python there's something desperately wrong - bear that in mind13:56
lkclmmm.... maaybe RITA13:56
lkcland definitely cavatools and gem513:56
lkclbut gem5 is an insanely-large codebase13:56
lkcloh - the PC obviously will be in there.13:57
lkcladdi 3,4,5 # PC=813:57
lkclcmpi 1,2,3,4 # PC=1213:57
lkclld 1,2(3) # PC=16 EA=0x1234567813:58
ghostmansd[m]Why PC is 8 for the first one?13:58
lkclif you literally expect that to be the input, it will be about 5 minutes work to make ISACaller produce that as output13:58
ghostmansd[m]Shouldn't be 4?13:58
lkclno reason at all, i just picked it as an example13:58
ghostmansd[m]Ah OK13:58
ghostmansd[m]Another question, shouldn't all these insns come as binaries?13:59
lkclbut it will matter in an iteratively-improved version, because PC is what the "fetch" comes from13:59
ghostmansd[m]I.e. 4 bytes at once13:59
lkclok that begins to tie in to the full capabilities of the simulator itself13:59
ghostmansd[m]Not as asm, but rather as a simple stream of insns13:59
lkclwhich means duplicating the simulator13:59
lkclwhich is the last thing we need13:59
lkclmy idea here is that ISACaller (or other simulator) *generates* a log file that this tool can use14:00
lkclif you have to decode the instructions in this tool it's doing too much.14:00
ghostmansd[m]Well, modeling fetch, decode, issue, execute stages is almost the simulator :-)14:00
ghostmansd[m]Ah so it's rather a trace walker14:01
lkclit isn't - because it's not actually going to execute the instructions. at all.14:01
lkclall it cares about is "what's the memory address being loaded or stored" and "what registers are used, and are they available/valid"14:01
lkclit doesn't care *at all* what the actual *values* are in those registers, nor the contents of the memory.14:01
lkcllet's say you have 2 instructions:14:02
lkcladdi 1,2,214:02
lkclmuli 3,1,214:02
lkclthe output from addi is used by muli14:02
lkcltherefore you *must* stall14:02
lkclyou don't care - at all - what the *contents* of register 1 2 or 3 are14:02
lkclyou care solely and exclusively "is the result of the add available in register 1 yet, no it isn't, oh dear we need to STALL until it is"14:03
lkclthat's an In-Order core14:03
lkclso the Model needs to go14:03
lkclcycle 1: i have fetched the add14:03
lkclcycle 2: i am decoding the add, AND i am fetching the mul14:04
lkclcycle 3: i am issuing the add, i am decoding the mul14:04
lkclcycle 4: i am EXECUTING the add, but the results are NOT READY THEREFORE I MUST STALL14:04
lkclcycle 4: i am stalled on fetching, i am executing the add14:04
lkclcycle 5: the add result is ready, i am WRITING the add, the MUL is unblocked, i can now ISSUE the add14:05
lkclcycle 6: i am EXECUTING the mul14:05
lkclcycle 7: the mul result is ready, i am writing the MUL14:05
lkclsorry, cycle 1 2 3 4 5 6 7 8 not 1234456714:06
lkclbut it is NOT cycle 123456 because of the additional STALL at  cycle 414:06
lkcl(because the mul needed the result of the add, which takes another 2 cycles to produce)14:06
lkcland thus the IPC is 0.75 *not* 1.014:07
lkclbecause of the 2 stalls in 8 cycles14:07
lkclso the crucial information is actually "how many stalls occurred"14:07
lkclhence that has to be Modelled14:07
lkclthe "Execute" class should *literally* be a queue14:08
lkcland it should contain elements that are extremely simple: "write result will be in GPR 5"14:08
lkcl"write result will be in FPR 7 and CR1"14:09
lkcland once you pop() that off the end of the queue14:09
lkclyou use it to clear the associated bit in the vector of "we are waiting for this register result"14:09
lkclnote: you don't pass the *result itself* down the queue.14:10
lkclwe don't care in the least bit what the contents of the regfiles are14:10
lkclwe care *only* about *which* register14:10
ghostmansd[m]Ok, input are the instructions. What is the output? Log which describes stalls and register contents?14:11
lkclno - not register contents14:11
lkcljust "a stall occurred here"14:11
lkclit would kinda be handy to have a table showing where each instruction is, through the pipelines?14:12
lkcland if "stall" occurs, then the table will show "blank entry" in that pipeline slot14:12
lkcli think that's probably the most visually-useful output (markdown)14:12
lkcl| fetch     | decode    | issue   | execute1  | execute2 |14:13
ghostmansd[m]What about jumps? These already per se need some bits of simulation, e.g. tracking the PC and the amount of the instructions.14:13
lkclthey're "just another instruction" at this point14:13
ghostmansd[m]Cough, I meant branches14:13
lkclbut later we can add a branch-predictor "thing" which issues (yet more) stalls14:13
ghostmansd[m]Yeah but they JUMP14:14
lkclbut for now just treat it as "just another instruction"14:14
lkclnot the pipeline's problem14:14
ghostmansd[m]Say to 4 instructions below14:14
lkclnot the pipeline's problem14:14
ghostmansd[m]No my point is, we need to know where they jump14:14
lkclinstructions don't actually care what the PC is (unless they have to read/write it)14:15
ghostmansd[m]To fetch the next insn14:15
lkclthe only place that matters is in the next phase where we "Model" the L1 and L2 caches14:15
ghostmansd[m]Don't branches write PC?14:15
lkcl(which will be later - don't worry about it for now)14:15
ghostmansd[m]Or, well, rather update14:15
lkclcorrect, but you can ignore them for now14:16
lkclPC is extremely weird: it is a non-existent concept as far as the execute pipelines are concerned14:16
lkcland is dealt with in a different/special way14:16
lkclyou have to "guess" which way the branch would go, and if you get it wrong, then, whoops, you STALL14:17
lkclbut for now treat it as "just another instruction"14:17
lkclthis will get sophisticated quite quickly and i don't want you overwhelmed14:18
lkclso one crucial thing about branch-conditional: it reads CR.14:18
lkcltherefore if you have this:14:18
lkclcmpi 0,1,214:18
lkclbc 0,...14:18
lkclguess what?14:18
lkclbc must STALL waiting for the output from cmpi14:19
lkclthat *is* important to model14:19
lkclbut the actual PC you can completely ignore - entirely - for now14:19
lkcl| fetch     | decode    | issue   | execute1  | execute2 |14:19
ghostmansd[m]OK, I'll think about it. But still: any other task in mind, a bit higher-level? :-)14:20
lkcl| addi 1,2,3   | empty     | empty     | empty    | empty   |14:20
lkcl| muli 3,1,2   | addi 1,2,3    | empty     | empty    | empty   |14:20
ghostmansd[m]I'm afraid with my current level of competence I'll be dealing with this for months :-)14:20
lkcl| STALL     | muli 3,1,2   | addi 1,2,3    | empty    | empty   |14:20
lkcllike i said: if it's more than 1,000 lines of code there's something horribly wrong14:21
lkclif the first iteration takes more than 5-7 days to code up, there's something very very wrong14:22
lkclbut this is actually a really important task for justification of commercial funding.14:22
lkclwe are getting "but what's the performance but what's the performance but what's the performance"14:23
lkcli'll be able to help advise - and probably "chip in" - once things get started14:26
lkclbut doing it myself, it just isn't going to happen.14:27
lkclmarkos_, had some thoughts - i have a sneaking suspicion you might need this for rounding:14:34
lkclround = sign(partialresult) * (abs(partialresult)+1)14:35
lkclthen shift it up (arithmetic-shift, because it's either 1 0 or -1)14:36
lkclyou get the idea.14:36
lkclif the (a+b) or (a-b) is negative, you want to *subtract* 1, if zero do nothing, if +ve *add* 1.14:37
lkclbecause - correct me if wrong - this is a *signed* instruction, you want "round towards zero"14:37
lkclin IEEE754 FP that's the default behaviour14:37
lkclby always adding one you are rounding **DOWN** negative partial-results14:37
markos_well, I'm trying to emulate the C code and the arm neon equivalents14:38
lkclahh :)14:38
lkclit *might* be the case that A is unsigned and B is signed14:39
markos_well, both operands have to be signed14:40
lkclbasically what i've described is likely to be a horrible bug in AV114:40
lkclbut one that if implemented correctly would be *so bad* in the number of instructions (certainly no longer just 8 instructions per butterfly) that it's been deliberately overlooked14:41
lkcleither that or we're missing something14:41
lkcli'm serious about this being a bug in AV1, if -ve A or B result is FLOORed but +ve A or B is CEILINGed, that's quite serious14:42
lkclif the c code is the reference is the spec, that's ultimately a bug in the AV1 specification14:42
markos_yes, if the function would be used on unprocessed/unfiltered data14:42
markos_but they are always fed data that is "clamped" within acceptable limits14:43
lkclif it's "offset" in some way such that the (new) A and (new) B are always +ve then that's fine14:43
lkclnew-A and new-B *have* to be unsigned results.14:43
lkclwhich doesn't smell right, to me14:44
markos_also, all DCT functions in the libs are fed signed data14:44
lkclit means that input-A and input-B have to be "offset" in some magic way which, frankly, is impossible to achieve14:44
markos_it's true you can get really bad results from the functions if you feed them bad data14:45
lkclhow can you possibly "arrange" the data such that for all butterflys input-A and input-B will *100% guaranteed* produce +ve result-A and result-B?14:45
markos_already bit by it doing the Arm port14:45
markos_nothing you can do really, it's like trying to write the perfect tan() function approximation and you keep feeding it inputs close to pi/214:46
markos_the cpu just cannot cope14:47
markos_well, it can, using a different algorithm/approximation14:47
lkclthis is way more fundamental - i think it's reasonable to assume you're going to get an even distribution of +ve and -ve values for input-A and input-B14:47
markos_well B is used for the cospi constants14:48
lkclbut we may be overthinking this: they may just have not performed any rounding at all14:48
markos_these are pretty known and indeed distributed14:48
markos_the RT, RA are from pixel data, and quite random, can be distributed, or not14:49
lkclyes.  ok  RT,RA (not A and B)14:49
lkclRT and RA i would expect to be 50% each +ve and -ve14:49
lkclso you have 25% -ve -ve14:49
lkcl25% -ve +ve14:49
lkcl25% +ve -ve14:49
lkcl25% +ve +ve14:49
lkclthere's just absolutely no way those can be "massaged" to 100% produce unsigned result-RT and result-RS14:50
markos_I could write some edge cases for that if you want14:51
markos_see how it behaves14:51
lkclprobably a good idea.14:51
markos_and compare with C/NEON results14:51
markos_well, C for 64-bit, NEON for 16/3214:52
lkcli bet you it's always rounded down. i.e. it's not an average-add14:52
lkclthat's if the c code is taken as the reference14:53
markos_I noticed earlier that the neon code did not do that, ie did not get the rounded down value, but I'll have to do a more proper research14:57
markos_but I cannot do it today, I have to finish some neon stuff first :-/15:00
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has quit IRC15:11
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has joined #libre-soc15:11
sadoon[m]Doing something absolutely bonkers today, might show you guys during the meeting hahah16:44
programmerjakelkcl: it rounds half-way cases towards +inf, otherwise towards nearest (due to the add before shifting). it doesn't need to have any logic for rounding towards zero. e.g. SH=4 prod=0xFFF4=-12 rounds correctly to -1 since it's closer to 0xFFF0, prod=0xFFF8=-8 rounds correctly to 0 since it's halfway, prod=0xFFFC rounds to 0 since it's closer16:59
programmerjaketry playing around with (v + 8) // 16 in python16:59
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has quit IRC17:28
*** choozy <choozy!~choozy@75-63-174-82.ftth.glasoperator.nl> has joined #libre-soc18:06
*** ghostmansd[m] <ghostmansd[m]!~ghostmans@> has joined #libre-soc18:21
lkclsadoon[m], :)18:22
lkclprogrammerjake, ahhh okaaay18:22
lkclghostmansd[m], i made a start https://git.libre-soc.org/?p=openpower-isa.git;a=commitdiff;h=e74dfbf1ecfb75affa90b7ce091e15764e1b9ac818:45
lkclnow let me put in some explanatory comments18:46
programmerjakemeeting in 6min19:55
*** octavius <octavius!~octavius@> has quit IRC21:30
*** choozy <choozy!~choozy@75-63-174-82.ftth.glasoperator.nl> has quit IRC21:46

Generated by irclog2html.py 2.17.1 by Marius Gedminas - find it at https://mg.pov.lt/irclog2html/!