Factorial on SubX


Ok, I think I understand calling conventions now.

Also coming face to face with the pain of debugging machine code πŸ˜€


Β· Β· Web Β· 1 Β· 0 Β· 4

Now that it can translate labels to offsets, SubX also warns on explicit use of error-prone raw offsets. Both when running and in Vim.

As I build up the ladder of abstractions I want to pull up the ladder behind me:

a) Unsafe programs will always work.
b) But unsafe programs will always emit warnings.

As long as SubX programs are always distributed in source form, it will be easy to check for unsafe code. Coming soon: type- and bounds-checking.


@freakazoid @h @rain1@nulled.red

SubX now supports basic file operation syscalls: akkartik.github.io/mu/html/sub

I've also made labels a little safer, so you can't call to inside a function, or jump to within a different function: akkartik.github.io/mu/html/sub

Next stop: socket syscalls! @h


@freakazoid @rain1@nulled.red @vertigo

@h Hmm, the socket syscalls are implemented differently on different platforms. That's dismaying. I'd been hoping to use a set of primitives so tiny that programs for it would work on all modern, extant *nixes. Now I probably need to start testing on Linux (top priority) and all the different *BSDs including Darwin (which is what I develop on).


@freakazoid @rain1@nulled.red @vertigo

More adventures with machine code

SubX now has a test harness, and support for string literals.

Current (increasingly silly) factorial program to compare with the parent toot: akkartik.name/images/20180923-

Two new features:

a) Autogenerated `run_tests` (line 26) which calls all functions starting with 'test_'.

b) String literals (line 31). They get transparently moved to the data segment and replaced with their address.


@haitch @vertigo @freakazoid @kragen

That isn't much progress for a month. I've been trying a few different things and learning a lot:

a) I spent some time looking at the GREAT stage0/Mes bootstrap project.

b) I tried to design a type-safe low-level language that could be converted line by line to machine code, but it turned out to be a bust (thanks @kragen!). It's possible if you give up on freeing heap memory.

I'm now convinced there are no shortcuts. Gotta build a real compiler in machine code.

@haitch @vertigo @freakazoid

There's only one problem: I don't know how to build a compiler. Not really. And definitely not in machine code. So I'm going to be fumbling around for a bit. Lots more wrong turns in my future.

I've been trying to port compilers.iecc.com/crenshaw to SubX. With tests. It's been slow going, because I have to think about how to make Crenshaw's primitives like `Error()` and `Abort()` testable.

@kragen @haitch @vertigo @freakazoid

I don't know if just learning to build a compiler will sustain my motivation, though. So some other ideas:

a) Some sort of idealized register allocator in Python or something. I've never built one, and my intuitions on how hard it is seem off.

b) Port Mu's fake screen/keyboard to SubX so that I can reimplement github.com/akkartik/mu/tree/ma. It was just too sluggish since Mu was interpreted. Even my 12 year old students quickly dropped it in favor of Vim.

@kragen @haitch @vertigo @freakazoid

@akkartik @h @freakazoid @rain1@nulled.red @vertigo So BSD sockets are implemented in different ways on different platforms?

Makes sense enough, I suppose, given epoll vs kqueue vs IOCP

@yumaikas I'm not sure. Darwin certainly does them differently from Linux.

Linux has a single `socketcall()` syscall (number 102) that multiplexes standard Posix functions like `socket()`, `connect()`, etc. man7.org/linux/man-pages/man2/; syscalls.kernelgrok.com.

Darwin has separate syscalls for each Posix function. `socket()` is 97, `connect()` is 98, and so on. opensource.apple.com/source/xn

Hopefully the BSDs all share a common approach. Bears investigation.

@h @freakazoid @rain1@nulled.red @vertigo

@akkartik @h @freakazoid @rain1@nulled.red @vertigo what (if anything) would keep you from linking to a library and calling that instead via SubX, since posix is a thing? Or is does SubX not have a C FFI set up?

@yumaikas Yes, I'll probably have to swap in a different library for each platform.

> what would keep you from linking to a library?

Sheer bull-headedness :) SubX requires nothing more than a kernel to run. Not even a linker. I'd like to preserve this property when it starts self-hosting.

> does SubX not have a C FFI set up?

Interop with C or Posix is an anti-goal. The whole goal is to eliminate fixed interfaces, not provide yet another ossified interface.

@h @freakazoid @rain1@nulled.red @vertigo

@akkartik It doesn't seem too difficult to provide a Linux-like stub to uniformise socketcall() across different systems. Even Windows, which originally was just stolen from BSD wholesale.

@vertigo @rain1 @freakazoid @yumaikas

@yumaikas @freakazoid @rain1 @vertigo @akkartik Er... Windows sockets were originally just BSD sockets.

@h Yeah, I've been expecting the need for libraries. That seems pretty timeless.

What I'm going through the five stages of grief about is the need to test on multiple platforms.

@yumaikas @freakazoid @rain1@nulled.red @vertigo

@akkartik I don't think it will be too painful, maybe you can get by with simple includes for now, no namespaces, no automatic choice of implementation for a given target arch, nothing of thesort. Plain includes seem relatively easier to implement.

@vertigo @rain1 @freakazoid @yumaikas

@yumaikas @freakazoid @rain1 @vertigo @akkartik
The only annoying task to implement plain includes is perhaps checking for recursive inclusions.

@akkartik But that, too, is unnecessary for a proof-of-concept generating stubs. You may only need #ifdef and #include equivalents. Or perhaps something like Fo's assembly modules, each in a separate file labeled for each target arch. If you do it that way you don't need tne annoyance of implementing #ifdef blocks. Just includes and nothing more.

@vertigo @rain1 @freakazoid @yumaikas

@h Mu has a tradition of loading all reasonable-looking files in its directory in a well-defined order. I may do that rather than includes.

Loading code from multiple files is not a big deal, I was planning on it anyway.

No, my pain is more about needing to spin up a VM or VPS with different OSs, and develop/test/debug on them.

You're right that it's a one-time thing when building the shims/polyfills. Just needs doing. I may start on self-hosting first.

@vertigo @rain1@nulled.red @freakazoid @yumaikas

@akkartik Yeah it doesn't even have to be a socketcall() port, it could be a BSD port since it's really Linux who went a different way. All of the BSDs, Darwin, and Windowsnd

@yumaikas @freakazoid @rain1 @vertigo

@vertigo @rain1 @freakazoid @yumaikas @akkartik

All the BSDs, Darwin, and Windows follow the BSD sockets api, so itmaymake more sense to only provide appropriate shims for Linux the other way around.

@h @akkartik @yumaikas @freakazoid @rain1@nulled.red Windows uses its DLL interface to invoke sockets calls; it does not rely on x86-style INT traps like Linux or its BSD equivalent. To work with Windows, you'll need to support dynamic loading and linking.

@vertigo Yeah, Windows is out of scope for the moment. I'm sure there are many more bugs hiding under that particular rock.

@h My sense is that the "BSD sockets API" is just at the level of C prototypes, just like Posix. Do you happen to have any pointers to how the API is implemented in syscalls in different BSDs?

@yumaikas @freakazoid @rain1@nulled.red

@akkartik I think the most straightforward guide you can even borrow code from directly is FASM. That will probably save you an awful lot of time.

Even if Windows is out of the scope, flatassembler also includes support for win32 calls and generation of PE32 binaries, so that's not a limitation (although @vertigo is right about Windows being a massive dynamic linking annoyance, with no officially published kernel apis)

@rain1 @freakazoid @yumaikas @vertigo

@yumaikas @freakazoid @rain1@nulled.red @vertigo @akkartik

Apologies, I was going to say something more, but my instance is experiencing difficulties, I'm going to be using this backup account temporarily today.

@h @haitch Thanks for the reminder about FASM. I may just drop SubX and switch to it.

I've been avoiding self-hosted languages/platforms; the circular dependency of semantics on previous versions bars my goal of thin, easy-to-traverse abstractions. But for an assembler it doesn't feel like as big a deal because of the 1-to-1 mapping between source and binary.

Unfortunately, FASM doesn't seem to have any socket support :( On any platform. So no shims here.

@yumaikas @freakazoid @rain1@nulled.red @vertigo

@akkartik Oh take a look at the programming samples. I think there's one sockets example, although that may be OS-specific, you can still take a look at the binary it builds.

@h @yumaikas @freakazoid @rain1@nulled.red @vertigo

@haitch @h Yes, of course, it's Assembly so one can do anything with it. But the codebase doesn't in itself *encode* how to make socket calls. On any platform, let alone multiple ones. `grep` returns 0 results.

Searching the net shows me plenty of examples -- and always there's the question of how to make it cross-platform.

So I could use FASM, but it seems independent of the need to figure out how sockets are implemented on different platforms.

@yumaikas @freakazoid @rain1@nulled.red @vertigo

@akkartik Why would you drop SubX? I was under the impression that your goals are different than flatassembler's.

@h @yumaikas @freakazoid @rain1@nulled.red @vertigo

@h @haitch

My broad goal is a platform that I can quickly drill into as deep as necessary. Without knowing the entire stack; JIT learning of the minimum necessary for my immediate purposes.

SubX is just a means to a sub-goal: conventional compilers are too 'thick' to permit easy hackery. Particularly simultaneously hacking within as well as atop them. The context switch is a vast chasm right now. A stack based on FASM would be a vast improvement on that.

@vertigo @yumaikas @freakazoid @rain1@nulled.red

Show newer

@akkartik @h @haitch @yumaikas @freakazoid @rain1@nulled.red I can honestly say that the project would not still be going ahead like it is now had I not read Chuck Moore's Programming a Problem-Oriented Language.

I can confirm that Forth is a language that, in the general case and if you keep things simple, can be bootstrapped from raw assembly.

My Kestrel-3/E2 port of DX-Forth is less than 10KB of code too.

(It'll get larger when I implement limited 9P support for it though.)

@vertigo Forth is definitely bootstrappable. But the extreme lack of checking makes it very hard to run with somebody else's code. Specifically the lack of good error messages when I pass the wrong number or type of arguments to a function.

It took me months to reluctantly move on and consider alternatives: lobste.rs/s/0myzye/thoughts_on

We've chatted briefly about this before, though perhaps I wasn't clear then: mastodon.social/@akkartik/1003

@h @haitch @yumaikas @freakazoid @rain1@nulled.red

Show newer
Show newer


Containers won't let me run BSD over Linux, will they?

@freakazoid @rain1@nulled.red @vertigo @yumaikas

@h @akkartik @freakazoid @rain1@nulled.red @vertigo Can docker/lxc run OSX/BSD and Windows on a linux machine? You're still taking time to spin up and configure 3-6 VMs when you start doing more serious testing, or having friends test on their machines


Register allocators are pretty fun - some heuristics applied to graph coloring, which is quite hard.

@haitch @vertigo @freakazoid @akkartik

@akkartik You can definitely write a real compiler without a register allocator at all. I haven't written one myself! @haitch @vertigo @freakazoid

@kragen Depends on where one draws the system boundary. In the Crenshaw compiler the error message is clearly part of what is being designed for. So it seems to me there should be some "early warning system" if a code change breaks its response to *incorrect* programs.

@haitch @vertigo

@akkartik Oh, sure, you could test from the user perspective what error message appears. But I think it would be better to indirect the error message through some sort of error code, and test for that, instead of the wording of the error message. @haitch @vertigo

@akkartik @kragen @haitch @freakazoid Honestly, my advice here is to read two books: "Programming a Problem Oriented Language" teaches you how to write your own Forth compiler from scratch (bare metal on up). There are no assembly listings in the book, because it's pure guidance, but it was instrumental in me getting DX-Forth working at all.

@akkartik @kragen @haitch @freakazoid

Second, if you're willing to tackle the more sophisticated compiler techniques needed to have a proper infix compiler, I would then recommend reading Compiler Construction, by Niklaus Wirth (see ethoberon.ethz.ch/WirthPubl/CB).

You don't need fancy register allocation to get a working compiler that meets 80% of your performance goals. Don't let that slow you down.

@akkartik @kragen @haitch @freakazoid
Next, I would strongly recommend reading cs.indiana.edu/~dyb/pubs/nano- . This was instrumental in getting my BSPL compiler prototype to work, and I'll be revisiting this again when I revise BSPL once more to port VertigOS to the .

@akkartik @kragen @haitch @freakazoid There seems to be this great fear of passes in compiler design. It's a one-pass compiler, or a two-pass compiler. Hogwash; do 56 passes if you must; all that matters is the final output. Register allocation can be one (or maybe a few) of those passes towards the end.

@vertigo historically, reducing the number of passes was a big win for reducing the complexity and improving interactive performance, in large part because the passes communicated via mass storage. The nanopass framework eliminates a lot of those costs, and of course hardware is fast now. @akkartik @haitch @freakazoid

@kragen Can you elaborate on how nanopass helps manage complexity? I've tried to read the papers, but I think I'm not yet enlightened.

@vertigo @haitch

@akkartik @kragen @haitch Think Unix pipelines.

Each pass is like a Unix filter -- it takes input, processes it in some *very* well defined way, and produces output. Each pass communicates with other passes via in-memory buffers, so no mass storage is required.

Regarding mass storage requirements, though, you can have nanopass compilers on a Commodore 64 if you wanted, by reducing the size of the compilation unit. BSPL works on individual colon definitions, for example, and uses mere KB.

@vertigo I see, yes I divide up work into functions that operate on the entire compilation unit.

Mu has 27 'transforms': akkartik.github.io/mu/html/012

SubX has 7 so far, but I added an additional notion of 'checks': akkartik.github.io/mu/html/sub

But the original nanopass paper describes designing a separate formally-defined language for each pass: cs.indiana.edu/~dyb/pubs/comme

(Unix pipelines also introduce concurrency and flow-control; I perhaps know too much for that analogy to be helpful.)

@kragen @haitch

@akkartik I'm not sold on anything thsat recommends many different languages to basically treat successive transforms of an essentially equivalent graph. Proofs for every lttle lang seem way overkill to me, when you can reuse good old graph theory on any graph.
Then, that may be just me and my hammer seeing it all as nails. And linguistically oriented overengineering in a mathematical shape may also be somebody else's hammer. Different approaches, but I know which I'd follow.

@vertigo @kragen

@haitch @akkartik @kragen I'm not sure where your contention lies. Can you explain how your idea and the idea of multiple nanopasses conflict?

NOTE: I'm *not* talking about the Nanopass Framework, which is a framework intended to automate the boilerplate of writing nanopasses, saving time in an educational context. I'm talking about the abstractions they are specific instances of.

@haitch @akkartik @kragen My apologies if I wasn't clear in this regard earlier.

@vertigo I get you and I'm not oppossed to the idea of multi-pass compilers. I'm highly biased against the proposed formal proofs expressed as various different languages. I just happen to see multiple passes as a sequence of graph transfor,s, for which you may have a formal description in one regu;ar off-the-shelf language borrowing from graph theory. Data structures of internal memory representation are just optimisations that run on machines.

@akkartik @kragen

@haitch @akkartik @kragen "against the proposed formal proofs expressed as various different languages" -- Ahh, gotcha. That's what I was confused over. Thanks for clarifying.

@vertigo But boilerplate isn't just a problem in an educational context. I'm particularly sensitive to it in machine code, where it's a maze of twisty passages mostly alike.

Also, the nanopass framework isn't just about superficial boilerplate. They were attacking the phase ordering problem. You write a phase expecting some feature to exist, but you inserted the phase where the feature isn't computed yet. Or isn't computed in some situations. Formal specification helps there.

@haitch @kragen

@vertigo To my mind, the framework didn't mutate the idea. The original paper I linked to thinks in terms of separate languages.

But I think I've been getting pedantic. You're right that many phases is a good thing as long as the format (not language) doesn't diverge too much between them. I think I follow where you and @kragen are coming from. You have some in-memory data structure for the program, and successive phases fill in different parts of it. Am I understanding you right?


Sign in to participate in the conversation

Server run by the main developers of the project 🐘 It is not focused on any particular niche interest - everyone is welcome as long as you follow our code of conduct!

<svg xmlns="http://www.w3.org/2000/svg"><symbol id="mastodon-svg-logo" viewBox="0 0 216.4144 232.00976"><path d="M107.86523 0C78.203984.2425 49.672422 3.4535937 33.044922 11.089844c0 0-32.97656262 14.752031-32.97656262 65.082031 0 11.525-.224375 25.306175.140625 39.919925 1.19750002 49.22 9.02375002 97.72843 54.53124962 109.77343 20.9825 5.55375 38.99711 6.71547 53.505856 5.91797 26.31125-1.45875 41.08203-9.38867 41.08203-9.38867l-.86914-19.08984s-18.80171 5.92758-39.91796 5.20508c-20.921254-.7175-43.006879-2.25516-46.390629-27.94141-.3125-2.25625-.46875-4.66938-.46875-7.20313 0 0 20.536953 5.0204 46.564449 6.21289 15.915.73001 30.8393-.93343 45.99805-2.74218 29.07-3.47125 54.38125-21.3818 57.5625-37.74805 5.0125-25.78125 4.59961-62.916015 4.59961-62.916015 0-50.33-32.97461-65.082031-32.97461-65.082031C166.80539 3.4535938 138.255.2425 108.59375 0h-.72852zM74.296875 39.326172c12.355 0 21.710234 4.749297 27.896485 14.248047l6.01367 10.080078 6.01563-10.080078c6.185-9.49875 15.54023-14.248047 27.89648-14.248047 10.6775 0 19.28156 3.753672 25.85156 11.076172 6.36875 7.3225 9.53907 17.218828 9.53907 29.673828v60.941408h-24.14454V81.869141c0-12.46875-5.24453-18.798829-15.73828-18.798829-11.6025 0-17.41797 7.508516-17.41797 22.353516v32.375002H96.207031V85.423828c0-14.845-5.815468-22.353515-17.417969-22.353516-10.49375 0-15.740234 6.330079-15.740234 18.798829v59.148439H38.904297V80.076172c0-12.455 3.171016-22.351328 9.541015-29.673828 6.568751-7.3225 15.172813-11.076172 25.851563-11.076172z" /></symbol></svg> <svg xmlns="http://www.w3.org/2000/svg"><symbol id="mastodon-svg-logo-full" viewBox="0 0 713.35878 175.8678"><path d="M160.55476 105.43125c-2.4125 12.40625-21.5975 25.9825-43.63375 28.61375-11.49125 1.3725-22.80375 2.63125-34.8675 2.07875-19.73-.90375-35.2975-4.71-35.2975-4.71 0 1.92125.11875 3.75.355 5.46 2.565 19.47 19.3075 20.6375 35.16625 21.18125 16.00625.5475 30.2575-3.9475 30.2575-3.9475l.65875 14.4725s-11.19625 6.01125-31.14 7.11625c-10.99875.605-24.65375-.27625-40.56-4.485C6.99851 162.08 1.06601 125.31.15851 88-.11899 76.9225.05226 66.47625.05226 57.74125c0-38.1525 24.99625-49.335 24.99625-49.335C37.65226 2.6175 59.27976.18375 81.76351 0h.5525c22.48375.18375 44.125 2.6175 56.72875 8.40625 0 0 24.99625 11.1825 24.99625 49.335 0 0 .3125 28.1475-3.48625 47.69" fill="#3088d4"/><path d="M34.65751 48.494c0-5.55375 4.5025-10.055 10.055-10.055 5.55375 0 10.055 4.50125 10.055 10.055 0 5.5525-4.50125 10.055-10.055 10.055-5.5525 0-10.055-4.5025-10.055-10.055M178.86476 60.69975v46.195h-18.30125v-44.8375c0-9.4525-3.9775-14.24875-11.9325-14.24875-8.79375 0-13.2025 5.69125-13.2025 16.94375V89.2935h-18.19375V64.75225c0-11.2525-4.40875-16.94375-13.2025-16.94375-7.955 0-11.9325 4.79625-11.9325 14.24875v44.8375H73.79851v-46.195c0-9.44125 2.40375-16.94375 7.2325-22.495 4.98-5.55 11.50125-8.395 19.595-8.395 9.36625 0 16.45875 3.59875 21.14625 10.79875l4.56 7.6425 4.55875-7.6425c4.68875-7.2 11.78-10.79875 21.1475-10.79875 8.09375 0 14.61375 2.845 19.59375 8.395 4.82875 5.55125 7.2325 13.05375 7.2325 22.495M241.91276 83.663625c3.77625-3.99 5.595-9.015 5.595-15.075 0-6.06-1.81875-11.085-5.595-14.9275-3.63625-3.99125-8.25375-5.91125-13.84875-5.91125-5.59625 0-10.2125 1.92-13.84875 5.91125-3.6375 3.8425-5.45625 8.8675-5.45625 14.9275 0 6.06 1.81875 11.085 5.45625 15.075 3.63625 3.8425 8.2525 5.76375 13.84875 5.76375 5.595 0 10.2125-1.92125 13.84875-5.76375m5.595-52.025h18.04625v73.9h-18.04625v-8.72125c-5.455 7.2425-13.01 10.79-22.80125 10.79-9.3725 0-17.34625-3.695-24.06125-11.23375-6.57375-7.5375-9.93125-16.84875-9.93125-27.785 0-10.78875 3.3575-20.10125 9.93125-27.63875 6.715-7.5375 14.68875-11.38 24.06125-11.38 9.79125 0 17.34625 3.5475 22.80125 10.78875v-8.72zM326.26951 67.258625c5.315 3.99 7.97375 9.60625 7.83375 16.7 0 7.53875-2.65875 13.45-8.11375 17.58875-5.45625 3.99125-12.03 6.06-20.00375 6.06-14.40875 0-24.20125-5.9125-29.3775-17.58875l15.66875-9.31c2.0975 6.35375 6.71375 9.60625 13.70875 9.60625 6.43375 0 9.6525-2.07 9.6525-6.35625 0-3.10375-4.1975-5.91125-12.73-8.1275-3.21875-.8875-5.87625-1.77375-7.97375-2.51375-2.9375-1.18125-5.455-2.5125-7.55375-4.1375-5.17625-3.99-7.83375-9.3125-7.83375-16.11 0-7.2425 2.5175-13.00625 7.55375-17.145 5.17625-4.28625 11.47-6.355 19.025-6.355 12.03 0 20.84375 5.1725 26.5775 15.66625l-15.38625 8.8675c-2.23875-5.02375-6.015-7.53625-11.19125-7.53625-5.45625 0-8.11375 2.06875-8.11375 6.05875 0 3.10375 4.19625 5.91125 12.73 8.12875 6.575 1.4775 11.75 3.695 15.5275 6.50375M383.626635 49.966125h-15.8075v30.7425c0 3.695 1.4 5.91125 4.0575 6.945 1.95875.74 5.875.8875 11.75.59125v17.29375c-12.16875 1.4775-20.9825.295-26.15875-3.69625-5.175-3.8425-7.69375-10.93625-7.69375-21.13375v-30.7425h-12.17v-18.3275h12.17v-14.9275l18.045-5.76375v20.69125h15.8075v18.3275zM441.124885 83.2205c3.6375-3.84375 5.455-8.72125 5.455-14.6325 0-5.91125-1.8175-10.78875-5.455-14.63125-3.6375-3.84375-8.11375-5.76375-13.57-5.76375-5.455 0-9.93125 1.92-13.56875 5.76375-3.4975 3.99-5.31625 8.8675-5.31625 14.63125 0 5.765 1.81875 10.6425 5.31625 14.6325 3.6375 3.8425 8.11375 5.76375 13.56875 5.76375 5.45625 0 9.9325-1.92125 13.57-5.76375m-39.86875 13.15375c-7.13375-7.5375-10.63125-16.70125-10.63125-27.78625 0-10.9375 3.4975-20.1 10.63125-27.6375 7.13375-7.5375 15.9475-11.38 26.29875-11.38 10.3525 0 19.165 3.8425 26.3 11.38 7.135 7.5375 10.77125 16.84875 10.77125 27.6375 0 10.9375-3.63625 20.24875-10.77125 27.78625-7.135 7.53875-15.8075 11.2325-26.3 11.2325-10.49125 0-19.165-3.69375-26.29875-11.2325M524.92126 83.663625c3.6375-3.99 5.455-9.015 5.455-15.075 0-6.06-1.8175-11.085-5.455-14.9275-3.63625-3.99125-8.25375-5.91125-13.84875-5.91125-5.59625 0-10.2125 1.92-13.98875 5.91125-3.63625 3.8425-5.45625 8.8675-5.45625 14.9275 0 6.06 1.82 11.085 5.45625 15.075 3.77625 3.8425 8.5325 5.76375 13.98875 5.76375 5.595 0 10.2125-1.92125 13.84875-5.76375m5.455-81.585h18.04625v103.46h-18.04625v-8.72125c-5.315 7.2425-12.87 10.79-22.66125 10.79-9.3725 0-17.485-3.695-24.2-11.23375-6.575-7.5375-9.9325-16.84875-9.9325-27.785 0-10.78875 3.3575-20.10125 9.9325-27.63875 6.715-7.5375 14.8275-11.38 24.2-11.38 9.79125 0 17.34625 3.5475 22.66125 10.78875v-38.28zM611.79626 83.2205c3.63625-3.84375 5.455-8.72125 5.455-14.6325 0-5.91125-1.81875-10.78875-5.455-14.63125-3.6375-3.84375-8.11375-5.76375-13.57-5.76375-5.455 0-9.9325 1.92-13.56875 5.76375-3.49875 3.99-5.31625 8.8675-5.31625 14.63125 0 5.765 1.8175 10.6425 5.31625 14.6325 3.63625 3.8425 8.11375 5.76375 13.56875 5.76375 5.45625 0 9.9325-1.92125 13.57-5.76375m-39.86875 13.15375c-7.135-7.5375-10.63125-16.70125-10.63125-27.78625 0-10.9375 3.49625-20.1 10.63125-27.6375 7.135-7.5375 15.9475-11.38 26.29875-11.38 10.3525 0 19.165 3.8425 26.3 11.38 7.135 7.5375 10.77125 16.84875 10.77125 27.6375 0 10.9375-3.63625 20.24875-10.77125 27.78625-7.135 7.53875-15.8075 11.2325-26.3 11.2325-10.49125 0-19.16375-3.69375-26.29875-11.2325M713.35876 60.163875v45.37375h-18.04625v-43.00875c0-4.8775-1.25875-8.5725-3.77625-11.38-2.37875-2.5125-5.73625-3.84375-10.0725-3.84375-10.2125 0-15.3875 6.06-15.3875 18.3275v39.905h-18.04625v-73.89875h18.04625v8.27625c4.33625-6.94625 11.19-10.345 20.84375-10.345 7.69375 0 13.98875 2.66 18.885 8.12875 5.035 5.46875 7.55375 12.85875 7.55375 22.465"/></symbol></svg>